1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.convert;
17
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Modifier;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23
24
25
26
27
28
29
30
31
32 public final class StringConvert {
33
34
35
36
37
38
39
40 public static final StringConvert INSTANCE = new StringConvert();
41
42
43
44
45 private final ConcurrentMap<Class<?>, StringConverter<?>> registered = new ConcurrentHashMap<Class<?>, StringConverter<?>>();
46
47
48
49
50
51
52
53
54
55 public StringConvert() {
56 this(true);
57 }
58
59
60
61
62
63
64
65
66
67
68
69 public StringConvert(boolean includeJdkConverters) {
70 if (includeJdkConverters) {
71 for (JDKStringConverter conv : JDKStringConverter.values()) {
72 registered.put(conv.getType(), conv);
73 }
74 registered.put(Boolean.TYPE, JDKStringConverter.BOOLEAN);
75 registered.put(Byte.TYPE, JDKStringConverter.BYTE);
76 registered.put(Short.TYPE, JDKStringConverter.SHORT);
77 registered.put(Integer.TYPE, JDKStringConverter.INTEGER);
78 registered.put(Long.TYPE, JDKStringConverter.LONG);
79 registered.put(Float.TYPE, JDKStringConverter.FLOAT);
80 registered.put(Double.TYPE, JDKStringConverter.DOUBLE);
81 registered.put(Character.TYPE, JDKStringConverter.CHARACTER);
82
83 tryRegister("java.time.Instant", "parse");
84 tryRegister("java.time.Duration", "parse");
85 tryRegister("java.time.LocalDate", "parse");
86 tryRegister("java.time.LocalTime", "parse");
87 tryRegister("java.time.LocalDateTime", "parse");
88 tryRegister("java.time.OffsetTime", "parse");
89 tryRegister("java.time.OffsetDateTime", "parse");
90 tryRegister("java.time.ZonedDateTime", "parse");
91 tryRegister("java.time.Year", "parse");
92 tryRegister("java.time.YearMonth", "parse");
93 tryRegister("java.time.MonthDay", "parse");
94 tryRegister("java.time.Period", "parse");
95 tryRegister("java.time.ZoneOffset", "of");
96 tryRegister("java.time.ZoneId", "of");
97
98 tryRegister("org.threeten.bp.Instant", "parse");
99 tryRegister("org.threeten.bp.Duration", "parse");
100 tryRegister("org.threeten.bp.LocalDate", "parse");
101 tryRegister("org.threeten.bp.LocalTime", "parse");
102 tryRegister("org.threeten.bp.LocalDateTime", "parse");
103 tryRegister("org.threeten.bp.OffsetTime", "parse");
104 tryRegister("org.threeten.bp.OffsetDateTime", "parse");
105 tryRegister("org.threeten.bp.ZonedDateTime", "parse");
106 tryRegister("org.threeten.bp.Year", "parse");
107 tryRegister("org.threeten.bp.YearMonth", "parse");
108 tryRegister("org.threeten.bp.MonthDay", "parse");
109 tryRegister("org.threeten.bp.Period", "parse");
110 tryRegister("org.threeten.bp.ZoneOffset", "of");
111 tryRegister("org.threeten.bp.ZoneId", "of");
112
113 tryRegister("javax.time.Instant", "parse");
114 tryRegister("javax.time.Duration", "parse");
115 tryRegister("javax.time.calendar.LocalDate", "parse");
116 tryRegister("javax.time.calendar.LocalTime", "parse");
117 tryRegister("javax.time.calendar.LocalDateTime", "parse");
118 tryRegister("javax.time.calendar.OffsetDate", "parse");
119 tryRegister("javax.time.calendar.OffsetTime", "parse");
120 tryRegister("javax.time.calendar.OffsetDateTime", "parse");
121 tryRegister("javax.time.calendar.ZonedDateTime", "parse");
122 tryRegister("javax.time.calendar.Year", "parse");
123 tryRegister("javax.time.calendar.YearMonth", "parse");
124 tryRegister("javax.time.calendar.MonthDay", "parse");
125 tryRegister("javax.time.calendar.Period", "parse");
126 tryRegister("javax.time.calendar.ZoneOffset", "of");
127 tryRegister("javax.time.calendar.ZoneId", "of");
128 tryRegister("javax.time.calendar.TimeZone", "of");
129 }
130 }
131
132
133
134
135
136
137 private void tryRegister(String className, String fromStringMethodName) {
138 try {
139 Class<?> cls = getClass().getClassLoader().loadClass(className);
140 registerMethods(cls, "toString", fromStringMethodName);
141 } catch (Exception ex) {
142
143 }
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157 @SuppressWarnings("unchecked")
158 public <T> String convertToString(T object) {
159 if (object == null) {
160 return null;
161 }
162 Class<T> cls = (Class<T>) object.getClass();
163 StringConverter<T> conv = findConverter(cls);
164 return conv.convertToString(object);
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178
179 public <T> String convertToString(Class<T> cls, T object) {
180 if (object == null) {
181 return null;
182 }
183 StringConverter<T> conv = findConverter(cls);
184 return conv.convertToString(object);
185 }
186
187
188
189
190
191
192
193
194
195
196
197
198 public <T> T convertFromString(Class<T> cls, String str) {
199 if (str == null) {
200 return null;
201 }
202 StringConverter<T> conv = findConverter(cls);
203 return conv.convertFromString(cls, str);
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 @SuppressWarnings("unchecked")
222 public <T> StringConverter<T> findConverter(final Class<T> cls) {
223 if (cls == null) {
224 throw new IllegalArgumentException("Class must not be null");
225 }
226 StringConverter<T> conv = (StringConverter<T>) registered.get(cls);
227 if (conv == null) {
228 if (cls == Object.class) {
229 throw new IllegalStateException("No registered converter found: " + cls);
230 }
231 Class<?> loopCls = cls.getSuperclass();
232 while (loopCls != null && conv == null) {
233 conv = (StringConverter<T>) registered.get(loopCls);
234 loopCls = loopCls.getSuperclass();
235 }
236 if (conv == null) {
237 conv = findAnnotationConverter(cls);
238 if (conv == null) {
239 throw new IllegalStateException("No registered converter found: " + cls);
240 }
241 }
242 registered.putIfAbsent(cls, conv);
243 }
244 return conv;
245 }
246
247
248
249
250
251
252
253
254 private <T> StringConverter<T> findAnnotationConverter(final Class<T> cls) {
255 Method toString = findToStringMethod(cls);
256 if (toString == null) {
257 return null;
258 }
259 Constructor<T> con = findFromStringConstructor(cls);
260 Method fromString = findFromStringMethod(cls, con == null);
261 if (con == null && fromString == null) {
262 throw new IllegalStateException("Class annotated with @ToString but not with @FromString");
263 }
264 if (con != null && fromString != null) {
265 throw new IllegalStateException("Both method and constructor are annotated with @FromString");
266 }
267 if (con != null) {
268 return new MethodConstructorStringConverter<T>(cls, toString, con);
269 } else {
270 return new MethodsStringConverter<T>(cls, toString, fromString);
271 }
272 }
273
274
275
276
277
278
279
280 private Method findToStringMethod(Class<?> cls) {
281 Method matched = null;
282 Class<?> loopCls = cls;
283 while (loopCls != null && matched == null) {
284 Method[] methods = loopCls.getDeclaredMethods();
285 for (Method method : methods) {
286 ToString toString = method.getAnnotation(ToString.class);
287 if (toString != null) {
288 if (matched != null) {
289 throw new IllegalStateException("Two methods are annotated with @ToString");
290 }
291 matched = method;
292 }
293 }
294 loopCls = loopCls.getSuperclass();
295 }
296 return matched;
297 }
298
299
300
301
302
303
304
305
306 private <T> Constructor<T> findFromStringConstructor(Class<T> cls) {
307 Constructor<T> con;
308 try {
309 con = cls.getDeclaredConstructor(String.class);
310 } catch (NoSuchMethodException ex) {
311 try {
312 con = cls.getDeclaredConstructor(CharSequence.class);
313 } catch (NoSuchMethodException ex2) {
314 return null;
315 }
316 }
317 FromString fromString = con.getAnnotation(FromString.class);
318 return fromString != null ? con : null;
319 }
320
321
322
323
324
325
326
327 private Method findFromStringMethod(Class<?> cls, boolean searchSuperclasses) {
328 Method matched = null;
329 Class<?> loopCls = cls;
330 while (loopCls != null && matched == null) {
331 Method[] methods = loopCls.getDeclaredMethods();
332 for (Method method : methods) {
333 FromString fromString = method.getAnnotation(FromString.class);
334 if (fromString != null) {
335 if (matched != null) {
336 throw new IllegalStateException("Two methods are annotated with @ToString");
337 }
338 matched = method;
339 }
340 }
341 if (searchSuperclasses == false) {
342 break;
343 }
344 loopCls = loopCls.getSuperclass();
345 }
346 return matched;
347 }
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363 public <T> void register(final Class<T> cls, StringConverter<T> converter) {
364 if (cls == null ) {
365 throw new IllegalArgumentException("Class must not be null");
366 }
367 if (converter == null) {
368 throw new IllegalArgumentException("StringConverter must not be null");
369 }
370 if (this == INSTANCE) {
371 throw new IllegalStateException("Global singleton cannot be extended");
372 }
373 registered.put(cls, converter);
374 }
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396 public <T> void register(final Class<T> cls, final ToStringConverter<T> toString, final FromStringConverter<T> fromString) {
397 if (fromString == null || toString == null) {
398 throw new IllegalArgumentException("Converters must not be null");
399 }
400 register(cls, new StringConverter<T>() {
401 public String convertToString(T object) {
402 return toString.convertToString(object);
403 }
404 public T convertFromString(Class<? extends T> cls, String str) {
405 return fromString.convertFromString(cls, str);
406 }
407 });
408 }
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429 public <T> void registerMethods(final Class<T> cls, String toStringMethodName, String fromStringMethodName) {
430 if (cls == null ) {
431 throw new IllegalArgumentException("Class must not be null");
432 }
433 if (toStringMethodName == null || fromStringMethodName == null) {
434 throw new IllegalArgumentException("Method names must not be null");
435 }
436 if (this == INSTANCE) {
437 throw new IllegalStateException("Global singleton cannot be extended");
438 }
439 Method toString = findToStringMethod(cls, toStringMethodName);
440 Method fromString = findFromStringMethod(cls, fromStringMethodName);
441 MethodsStringConverter<T> converter = new MethodsStringConverter<T>(cls, toString, fromString);
442 registered.putIfAbsent(cls, converter);
443 }
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463 public <T> void registerMethodConstructor(final Class<T> cls, String toStringMethodName) {
464 if (cls == null ) {
465 throw new IllegalArgumentException("Class must not be null");
466 }
467 if (toStringMethodName == null) {
468 throw new IllegalArgumentException("Method name must not be null");
469 }
470 if (this == INSTANCE) {
471 throw new IllegalStateException("Global singleton cannot be extended");
472 }
473 Method toString = findToStringMethod(cls, toStringMethodName);
474 Constructor<T> fromString = findFromStringConstructorByType(cls);
475 MethodConstructorStringConverter<T> converter = new MethodConstructorStringConverter<T>(cls, toString, fromString);
476 registered.putIfAbsent(cls, converter);
477 }
478
479
480
481
482
483
484
485
486 private Method findToStringMethod(Class<?> cls, String methodName) {
487 Method m;
488 try {
489 m = cls.getMethod(methodName);
490 } catch (NoSuchMethodException ex) {
491 throw new IllegalArgumentException(ex);
492 }
493 if (Modifier.isStatic(m.getModifiers())) {
494 throw new IllegalArgumentException("Method must not be static: " + methodName);
495 }
496 return m;
497 }
498
499
500
501
502
503
504
505
506 private Method findFromStringMethod(Class<?> cls, String methodName) {
507 Method m;
508 try {
509 m = cls.getMethod(methodName, String.class);
510 } catch (NoSuchMethodException ex) {
511 try {
512 m = cls.getMethod(methodName, CharSequence.class);
513 } catch (NoSuchMethodException ex2) {
514 throw new IllegalArgumentException("Method not found", ex2);
515 }
516 }
517 if (Modifier.isStatic(m.getModifiers()) == false) {
518 throw new IllegalArgumentException("Method must be static: " + methodName);
519 }
520 return m;
521 }
522
523
524
525
526
527
528
529
530 private <T> Constructor<T> findFromStringConstructorByType(Class<T> cls) {
531 try {
532 return cls.getDeclaredConstructor(String.class);
533 } catch (NoSuchMethodException ex) {
534 try {
535 return cls.getDeclaredConstructor(CharSequence.class);
536 } catch (NoSuchMethodException ex2) {
537 throw new IllegalArgumentException("Constructor not found", ex2);
538 }
539 }
540 }
541
542
543
544
545
546
547
548 @Override
549 public String toString() {
550 return getClass().getSimpleName();
551 }
552
553 }