1 package com.fasterxml.jackson.databind.convert;
2 
3 import java.io.File;
4 import java.net.InetAddress;
5 import java.net.InetSocketAddress;
6 import java.net.URI;
7 import java.net.URL;
8 import java.nio.charset.Charset;
9 import java.util.Calendar;
10 import java.util.Currency;
11 import java.util.Date;
12 import java.util.GregorianCalendar;
13 import java.util.Locale;
14 import java.util.TimeZone;
15 import java.util.UUID;
16 import java.util.regex.Pattern;
17 
18 import com.fasterxml.jackson.databind.BaseMapTest;
19 import com.fasterxml.jackson.databind.JavaType;
20 import com.fasterxml.jackson.databind.ObjectMapper;
21 import com.fasterxml.jackson.databind.cfg.CoercionAction;
22 import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
23 import com.fasterxml.jackson.databind.exc.MismatchedInputException;
24 
25 public class CoerceMiscScalarsTest extends BaseMapTest
26 {
27     private final ObjectMapper DEFAULT_MAPPER = sharedMapper();
28 
29     private final ObjectMapper MAPPER_EMPTY_TO_EMPTY;
30     {
31         MAPPER_EMPTY_TO_EMPTY = newJsonMapper();
32         MAPPER_EMPTY_TO_EMPTY.coercionConfigDefaults()
33             .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
34     }
35 
36     private final ObjectMapper MAPPER_EMPTY_TO_TRY_CONVERT;
37     {
38         MAPPER_EMPTY_TO_TRY_CONVERT = newJsonMapper();
39         MAPPER_EMPTY_TO_TRY_CONVERT.coercionConfigDefaults()
40             .setCoercion(CoercionInputShape.EmptyString, CoercionAction.TryConvert);
41     }
42 
43     private final ObjectMapper MAPPER_EMPTY_TO_NULL;
44     {
45         MAPPER_EMPTY_TO_NULL = newJsonMapper();
46         MAPPER_EMPTY_TO_NULL.coercionConfigDefaults()
47             .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull);
48     }
49 
50     private final ObjectMapper MAPPER_EMPTY_TO_FAIL;
51     {
52         MAPPER_EMPTY_TO_FAIL = newJsonMapper();
53         MAPPER_EMPTY_TO_FAIL.coercionConfigDefaults()
54             .setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail);
55     }
56 
57     private final String JSON_EMPTY = quote("");
58 
59     /*
60     /********************************************************
61     /* Test methods, defaults (legacy)
62     /********************************************************
63      */
64 
testScalarDefaultsFromEmpty()65     public void testScalarDefaultsFromEmpty() throws Exception
66     {
67         // mostly as null, with some exceptions
68 
69         _testScalarEmptyToNull(DEFAULT_MAPPER, File.class);
70         _testScalarEmptyToNull(DEFAULT_MAPPER, URL.class);
71 
72         _testScalarEmptyToEmpty(DEFAULT_MAPPER, URI.class,
73                 URI.create(""));
74 
75         _testScalarEmptyToNull(DEFAULT_MAPPER, Class.class);
76         _testScalarEmptyToNull(DEFAULT_MAPPER, JavaType.class);
77         _testScalarEmptyToNull(DEFAULT_MAPPER, Currency.class);
78         _testScalarEmptyToNull(DEFAULT_MAPPER, Pattern.class);
79 
80         _testScalarEmptyToEmpty(DEFAULT_MAPPER, Locale.class,
81                 Locale.ROOT);
82 
83         _testScalarEmptyToNull(DEFAULT_MAPPER, Charset.class);
84         _testScalarEmptyToNull(DEFAULT_MAPPER, TimeZone.class);
85         _testScalarEmptyToNull(DEFAULT_MAPPER, InetAddress.class);
86         _testScalarEmptyToNull(DEFAULT_MAPPER, InetSocketAddress.class);
87     }
88 
89     /*
90     /********************************************************
91     /* Test methods, successful coercions from empty String
92     /********************************************************
93      */
94 
testScalarEmptyToNull()95     public void testScalarEmptyToNull() throws Exception
96     {
97         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, File.class);
98         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, URL.class);
99         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, URI.class);
100         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Class.class);
101         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, JavaType.class);
102         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Currency.class);
103         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Pattern.class);
104         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Locale.class);
105         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Charset.class);
106         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, TimeZone.class);
107         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, InetAddress.class);
108         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, InetSocketAddress.class);
109     }
110 
testScalarEmptyToEmpty()111     public void testScalarEmptyToEmpty() throws Exception
112     {
113         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, File.class);
114         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, URL.class);
115 
116         _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, URI.class,
117                 URI.create(""));
118 
119         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, Class.class);
120         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, JavaType.class);
121         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, Currency.class);
122         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, Pattern.class);
123 
124         _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, Locale.class,
125                 Locale.ROOT);
126 
127         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, Charset.class);
128         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, TimeZone.class);
129         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, InetAddress.class);
130         _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, InetSocketAddress.class);
131     }
132 
testScalarEmptyToTryConvert()133     public void testScalarEmptyToTryConvert() throws Exception
134     {
135         // Should be same as `AsNull` for most but not all
136         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, File.class);
137         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, URL.class);
138 
139         _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_TRY_CONVERT, URI.class,
140                 URI.create(""));
141 
142         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Class.class);
143         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, JavaType.class);
144         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Currency.class);
145         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Pattern.class);
146 
147         _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_TRY_CONVERT, Locale.class,
148                 Locale.ROOT);
149 
150         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Charset.class);
151         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, TimeZone.class);
152         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, InetAddress.class);
153         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, InetSocketAddress.class);
154     }
155 
156     /*
157     /********************************************************
158     /* Test methods, failed coercions from empty String
159     /********************************************************
160      */
161 
testScalarsFailFromEmpty()162     public void testScalarsFailFromEmpty() throws Exception
163     {
164         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, File.class);
165         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, URL.class);
166         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, URI.class);
167         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Class.class);
168         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, JavaType.class);
169         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Currency.class);
170         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Pattern.class);
171         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Locale.class);
172         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Charset.class);
173         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, TimeZone.class);
174         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, InetAddress.class);
175         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, InetSocketAddress.class);
176     }
177 
178     /*
179     /********************************************************
180     /* Test methods, (more) special type(s)
181     /********************************************************
182      */
183 
184     // UUID is quite compatible, but not exactly due to historical reasons;
185     // also uses custom subtype, so test separately
186 
testUUIDCoercions()187     public void testUUIDCoercions() throws Exception
188     {
189         // Coerce to `null` both by default, "TryConvert" and explicit
190         _testScalarEmptyToNull(DEFAULT_MAPPER, UUID.class);
191         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, UUID.class);
192         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, UUID.class);
193 
194         // but allow separate "empty" value is specifically requested
195         _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, UUID.class,
196                 new UUID(0L, 0L));
197 
198         // allow forcing failure, too
199         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, UUID.class);
200 
201         // and allow failure with specifically configured per-class override, too
202         ObjectMapper failMapper = newJsonMapper();
203         failMapper.coercionConfigFor(UUID.class)
204             .setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail);
205         _verifyScalarToFail(failMapper, UUID.class);
206     }
207 
208     // StringBuilder is its own special type, since it naturally maps
209     // from String values, hence separate testing
testStringBuilderCoercions()210     public void testStringBuilderCoercions() throws Exception
211     {
212         // should result in an "empty" StringBuilder for all valid settings
213         _checkEmptyStringBuilder(DEFAULT_MAPPER.readValue(JSON_EMPTY, StringBuilder.class));
214         _checkEmptyStringBuilder(MAPPER_EMPTY_TO_EMPTY.readValue(JSON_EMPTY, StringBuilder.class));
215         _checkEmptyStringBuilder(MAPPER_EMPTY_TO_TRY_CONVERT.readValue(JSON_EMPTY, StringBuilder.class));
216         _checkEmptyStringBuilder(MAPPER_EMPTY_TO_NULL.readValue(JSON_EMPTY, StringBuilder.class));
217         // and even alleged failure should not result in that since it's not coercion
218         _checkEmptyStringBuilder(MAPPER_EMPTY_TO_FAIL.readValue(JSON_EMPTY, StringBuilder.class));
219     }
220 
_checkEmptyStringBuilder(StringBuilder sb)221     private void _checkEmptyStringBuilder(StringBuilder sb) {
222         assertNotNull(sb);
223         assertEquals(0, sb.length());
224     }
225 
226     // Date, Calendar also included here for convenience
227 
testLegacyDateTimeCoercions()228     public void testLegacyDateTimeCoercions() throws Exception
229     {
230         // Coerce to `null` both by default, "TryConvert" and explicit
231         _testScalarEmptyToNull(DEFAULT_MAPPER, Calendar.class);
232         _testScalarEmptyToNull(DEFAULT_MAPPER, Date.class);
233         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Calendar.class);
234         _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Date.class);
235         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Calendar.class);
236         _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Date.class);
237 
238         // but allow separate "empty" value is specifically requested
239         Calendar emptyCal = new GregorianCalendar();
240         emptyCal.setTimeInMillis(0L);
241 //        _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, Calendar.class, emptyCal);
242         _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, Date.class, new Date(0L));
243 
244         // allow forcing failure, too
245         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Calendar.class);
246         _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Date.class);
247     }
248 
249     /*
250     /********************************************************
251     /* Second-level test helper methods
252     /********************************************************
253      */
254 
_testScalarEmptyToNull(ObjectMapper mapper, Class<?> target)255     private void _testScalarEmptyToNull(ObjectMapper mapper, Class<?> target) throws Exception
256     {
257         assertNull(mapper.readerFor(target).readValue(JSON_EMPTY));
258     }
259 
_testScalarEmptyToEmpty(ObjectMapper mapper, Class<?> target, Object emptyValue)260     private void _testScalarEmptyToEmpty(ObjectMapper mapper,
261             Class<?> target, Object emptyValue) throws Exception
262     {
263         Object result = mapper.readerFor(target).readValue(JSON_EMPTY);
264         if (result == null) {
265             fail("Expected empty, non-null value for "+target.getName()+", got null");
266         }
267         assertEquals(emptyValue, result);
268     }
269 
_verifyScalarToFail(ObjectMapper mapper, Class<?> target)270     private void _verifyScalarToFail(ObjectMapper mapper, Class<?> target) throws Exception
271     {
272         try {
273             /*Object result =*/ mapper.readerFor(target)
274                 .readValue(JSON_EMPTY);
275             fail("Should not pass");
276         } catch (MismatchedInputException e) {
277             verifyException(e, "Cannot coerce empty String ");
278             verifyException(e, " to `"+target.getName());
279         }
280     }
281 }
282