1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.convert;
20
21 import java.sql.Time;
22 import java.sql.Timestamp;
23 import java.text.DateFormat;
24 import java.text.ParseException;
25 import java.text.SimpleDateFormat;
26 import java.util.Calendar;
27 import java.util.Date;
28 import java.util.Locale;
29 import java.util.TimeZone;
30
31
32 public class DateTimeConverters implements ConverterLoader {
33
34
35
36
37 public static final String CALENDAR_FORMAT = "EEE MMM dd HH:mm:ss.SSS z yyyy";
38
39
40
41 public static final String JDBC_DATE_FORMAT = "yyyy-MM-dd";
42
43
44
45 public static final String JDBC_TIME_FORMAT = "HH:mm:ss";
46
47
48
49
50
51
52
53 protected static DateFormat toDateFormat(TimeZone tz) {
54 DateFormat df = new SimpleDateFormat(JDBC_DATE_FORMAT);
55 df.setTimeZone(tz);
56 return df;
57 }
58
59
60
61
62
63
64
65
66
67
68
69 protected static DateFormat toDateTimeFormat(String dateTimeFormat, TimeZone tz, Locale locale) {
70 DateFormat df = null;
71 if (Util.isEmpty(dateTimeFormat)) {
72 df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
73 } else {
74 df = new SimpleDateFormat(dateTimeFormat);
75 }
76 df.setTimeZone(tz);
77 return df;
78 }
79
80
81
82
83
84
85
86 protected static DateFormat toTimeFormat(TimeZone tz) {
87 DateFormat df = new SimpleDateFormat(JDBC_TIME_FORMAT);
88 df.setTimeZone(tz);
89 return df;
90 }
91
92 public void loadConverters() {
93 Converters.loadContainedConverters(DateTimeConverters.class);
94 Converters.registerConverter(new GenericDateToLong<java.util.Date>(java.util.Date.class));
95 Converters.registerConverter(new GenericDateToLong<java.sql.Date>(java.sql.Date.class));
96 Converters.registerConverter(new GenericDateToLong<java.sql.Time>(java.sql.Time.class));
97 Converters.registerConverter(new GenericDateToLong<java.sql.Timestamp>(java.sql.Timestamp.class));
98 Converters.registerConverter(new GenericSingletonToList<java.util.Calendar>(java.util.Calendar.class));
99 Converters.registerConverter(new GenericSingletonToList<java.util.Date>(java.util.Date.class));
100 Converters.registerConverter(new GenericSingletonToList<java.util.TimeZone>(java.util.TimeZone.class));
101 Converters.registerConverter(new GenericSingletonToList<java.sql.Date>(java.sql.Date.class));
102 Converters.registerConverter(new GenericSingletonToList<java.sql.Time>(java.sql.Time.class));
103 Converters.registerConverter(new GenericSingletonToList<java.sql.Timestamp>(java.sql.Timestamp.class));
104 Converters.registerConverter(new GenericSingletonToSet<java.util.Calendar>(java.util.Calendar.class));
105 Converters.registerConverter(new GenericSingletonToSet<java.util.Date>(java.util.Date.class));
106 Converters.registerConverter(new GenericSingletonToSet<java.util.TimeZone>(java.util.TimeZone.class));
107 Converters.registerConverter(new GenericSingletonToSet<java.sql.Date>(java.sql.Date.class));
108 Converters.registerConverter(new GenericSingletonToSet<java.sql.Time>(java.sql.Time.class));
109 Converters.registerConverter(new GenericSingletonToSet<java.sql.Timestamp>(java.sql.Timestamp.class));
110 }
111
112
113
114
115 public static class CalendarToDate extends AbstractConverter<Calendar, Date> {
116 public CalendarToDate() {
117 super(Calendar.class, Date.class);
118 }
119
120 @Override
121 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
122 return Util.instanceOf(sourceClass, this.getSourceClass()) && Date.class.equals(targetClass);
123 }
124
125 public Date convert(Calendar obj) throws ConversionException {
126 return obj.getTime();
127 }
128 }
129
130
131
132
133 public static class CalendarToLong extends AbstractConverter<Calendar, Long> {
134 public CalendarToLong() {
135 super(Calendar.class, Long.class);
136 }
137
138
139
140
141 public Long convert(Calendar obj) throws ConversionException {
142 return obj.getTimeInMillis();
143 }
144 }
145
146
147
148
149 public static class CalendarToString extends AbstractLocalizedConverter<Calendar, String> {
150 public CalendarToString() {
151 super(Calendar.class, String.class);
152 }
153
154
155
156
157
158
159 public String convert(Calendar obj) throws ConversionException {
160 DateFormat df = new SimpleDateFormat(CALENDAR_FORMAT);
161 df.setCalendar(obj);
162 return df.format(obj.getTime());
163 }
164
165
166
167
168
169
170
171 public String convert(Calendar obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
172 DateFormat df = toDateTimeFormat(formatString == null ? CALENDAR_FORMAT : formatString, timeZone, locale);
173 df.setCalendar(obj);
174 return df.format(obj.getTime());
175 }
176 }
177
178
179
180
181 public static class CalendarToTimestamp extends AbstractConverter<Calendar, Timestamp> {
182 public CalendarToTimestamp() {
183 super(Calendar.class, Timestamp.class);
184 }
185
186 @Override
187 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
188 return Util.instanceOf(sourceClass, this.getSourceClass()) && Timestamp.class.equals(targetClass);
189 }
190
191 public Timestamp convert(Calendar obj) throws ConversionException {
192 return new Timestamp(obj.getTimeInMillis());
193 }
194 }
195
196
197
198
199
200 public static class DateToCalendar extends GenericLocalizedConverter<Date, Calendar> {
201 public DateToCalendar() {
202 super(Date.class, Calendar.class);
203 }
204
205 public Calendar convert(Date obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
206 Calendar cal = Calendar.getInstance(timeZone, locale);
207 cal.setTime(obj);
208 return cal;
209 }
210 }
211
212
213
214
215
216 public static class DateToSqlDate extends AbstractConverter<Date, java.sql.Date> {
217 public DateToSqlDate() {
218 super(Date.class, java.sql.Date.class);
219 }
220
221 @Override
222 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
223 return (java.util.Date.class.equals(sourceClass) || java.sql.Timestamp.class.equals(sourceClass)) && java.sql.Date.class.equals(targetClass);
224 }
225
226
227
228
229 @SuppressWarnings("deprecation")
230 public java.sql.Date convert(Date obj) throws ConversionException {
231 Calendar cal = Calendar.getInstance();
232 cal.setTimeInMillis(obj.getTime());
233 return new java.sql.Date(cal.get(Calendar.YEAR) - 1900, cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
234 }
235 }
236
237
238
239
240
241 public static class DateToSqlTime extends AbstractConverter<java.util.Date, java.sql.Time> {
242 public DateToSqlTime() {
243 super(Date.class, java.sql.Time.class);
244 }
245
246 @Override
247 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
248 return sourceClass == this.getSourceClass() && targetClass == this.getTargetClass();
249 }
250
251 @SuppressWarnings("deprecation")
252 public java.sql.Time convert(Date obj) throws ConversionException {
253 Calendar cal = Calendar.getInstance();
254 cal.setTimeInMillis(obj.getTime());
255 return new java.sql.Time(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND));
256 }
257 }
258
259
260
261
262
263 public static class DateToString extends AbstractLocalizedConverter<Date, String> {
264 public DateToString() {
265 super(Date.class, String.class);
266 }
267
268 @Override
269 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
270 return Date.class.equals(sourceClass) && String.class.equals(targetClass);
271 }
272
273
274
275
276
277
278 public String convert(Date obj) throws ConversionException {
279 DateFormat df = new SimpleDateFormat(CALENDAR_FORMAT);
280 return df.format(obj);
281 }
282
283
284
285
286
287
288
289 public String convert(Date obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
290 DateFormat df = toDateTimeFormat(formatString == null ? CALENDAR_FORMAT : formatString, timeZone, locale);
291 return df.format(obj);
292 }
293 }
294
295
296
297
298
299 public static class DateToTimestamp extends AbstractConverter<Date, java.sql.Timestamp> {
300 public DateToTimestamp() {
301 super(Date.class, java.sql.Timestamp.class);
302 }
303
304 @Override
305 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
306 return (java.util.Date.class.equals(sourceClass) || java.sql.Timestamp.class.equals(sourceClass)) && java.sql.Timestamp.class.equals(targetClass);
307 }
308
309
310
311
312 public java.sql.Timestamp convert(Date obj) throws ConversionException {
313 return new java.sql.Timestamp(obj.getTime());
314 }
315 }
316
317
318
319
320
321 public static class GenericDateToLong<S extends Date> extends AbstractConverter<S, Long> {
322 public GenericDateToLong(Class<S> source) {
323 super(source, Long.class);
324 }
325
326
327
328
329 public Long convert(S obj) throws ConversionException {
330 return obj.getTime();
331 }
332 }
333
334 public static abstract class GenericLocalizedConverter<S, T> extends AbstractLocalizedConverter<S, T> {
335 protected GenericLocalizedConverter(Class<S> sourceClass, Class<T> targetClass) {
336 super(sourceClass, targetClass);
337 }
338
339 public T convert(S obj) throws ConversionException {
340 return convert(obj, Locale.getDefault(), TimeZone.getDefault(), null);
341 }
342
343 public T convert(S obj, Locale locale, TimeZone timeZone) throws ConversionException {
344 return convert(obj, locale, timeZone, null);
345 }
346 }
347
348
349
350
351
352 public static class LongToCalendar extends AbstractLocalizedConverter<Long, Calendar> {
353 public LongToCalendar() {
354 super(Long.class, Calendar.class);
355 }
356
357
358
359
360
361 public Calendar convert(Long obj) throws ConversionException {
362 Calendar cal = Calendar.getInstance();
363 cal.setTimeInMillis(obj);
364 return cal;
365 }
366
367
368
369
370
371
372 public Calendar convert(Long obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
373 Calendar cal = Calendar.getInstance(timeZone, locale);
374 cal.setTimeInMillis(obj);
375 return cal;
376 }
377 }
378
379
380
381
382
383 public static class LongToDate extends AbstractConverter<Long, Date> {
384 public LongToDate() {
385 super(Long.class, Date.class);
386 }
387
388 @Override
389 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
390 return Long.class.equals(sourceClass) && Date.class.equals(targetClass);
391 }
392
393
394
395
396 public Date convert(Long obj) throws ConversionException {
397 return new Date(obj.longValue());
398 }
399 }
400
401
402
403
404
405 public static class LongToSqlDate extends AbstractConverter<Long, java.sql.Date> {
406 public LongToSqlDate() {
407 super(Long.class, java.sql.Date.class);
408 }
409
410 @Override
411 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
412 return Long.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass);
413 }
414
415
416
417
418 public java.sql.Date convert(Long obj) throws ConversionException {
419 return new java.sql.Date(obj.longValue());
420 }
421 }
422
423
424
425
426
427 public static class LongToSqlTime extends AbstractConverter<Long, java.sql.Time> {
428 public LongToSqlTime() {
429 super(Long.class, java.sql.Time.class);
430 }
431
432 @Override
433 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
434 return Long.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass);
435 }
436
437
438
439
440 public java.sql.Time convert(Long obj) throws ConversionException {
441 return new java.sql.Time(obj.longValue());
442 }
443 }
444
445
446
447
448
449 public static class LongToTimestamp extends AbstractConverter<Long, java.sql.Timestamp> {
450 public LongToTimestamp() {
451 super(Long.class, java.sql.Timestamp.class);
452 }
453
454 @Override
455 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
456 return Long.class.equals(sourceClass) && java.sql.Timestamp.class.equals(targetClass);
457 }
458
459
460
461
462 public java.sql.Timestamp convert(Long obj) throws ConversionException {
463 return new java.sql.Timestamp(obj.longValue());
464 }
465 }
466
467
468
469
470
471 public static class SqlDateToDate extends AbstractConverter<java.sql.Date, Date> {
472 public SqlDateToDate() {
473 super(java.sql.Date.class, Date.class);
474 }
475
476 @Override
477 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
478 return java.sql.Date.class.equals(sourceClass) && java.util.Date.class.equals(targetClass);
479 }
480
481
482
483
484 public Date convert(java.sql.Date obj) throws ConversionException {
485 return new Date(obj.getTime());
486 }
487 }
488
489
490
491
492
493 public static class SqlDateToString extends AbstractLocalizedConverter<java.sql.Date, String> {
494 public SqlDateToString() {
495 super(java.sql.Date.class, String.class);
496 }
497
498 @Override
499 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
500 return java.sql.Date.class.equals(sourceClass) && String.class.equals(targetClass);
501 }
502
503 public String convert(java.sql.Date obj) throws ConversionException {
504 return obj.toString();
505 }
506
507
508
509
510
511
512
513 public String convert(java.sql.Date obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
514 DateFormat df = toDateFormat(timeZone);
515 return df.format(obj);
516 }
517 }
518
519
520
521
522
523 public static class SqlDateToTimestamp extends AbstractConverter<java.sql.Date, java.sql.Timestamp> {
524 public SqlDateToTimestamp() {
525 super(java.sql.Date.class, java.sql.Timestamp.class);
526 }
527
528 @Override
529 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
530 return java.sql.Date.class.equals(sourceClass) && java.sql.Timestamp.class.equals(targetClass);
531 }
532
533
534
535
536 public java.sql.Timestamp convert(java.sql.Date obj) throws ConversionException {
537 return new java.sql.Timestamp(obj.getTime());
538 }
539 }
540
541
542
543
544
545 public static class SqlTimeToString extends AbstractLocalizedConverter<java.sql.Time, String> {
546 public SqlTimeToString() {
547 super(java.sql.Time.class, String.class);
548 }
549
550 @Override
551 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
552 return java.sql.Time.class.equals(sourceClass) && String.class.equals(targetClass);
553 }
554
555 public String convert(java.sql.Time obj) throws ConversionException {
556 return obj.toString();
557 }
558
559
560
561
562
563
564
565 public String convert(java.sql.Time obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
566 DateFormat df = toTimeFormat(timeZone);
567 return df.format(obj);
568 }
569 }
570
571
572
573
574
575 public static class StringToCalendar extends AbstractLocalizedConverter<String, Calendar> {
576 public StringToCalendar() {
577 super(String.class, Calendar.class);
578 }
579
580
581
582
583
584
585 public Calendar convert(String obj) throws ConversionException {
586 try {
587 DateFormat df = new SimpleDateFormat(CALENDAR_FORMAT);
588 Calendar cal = Calendar.getInstance();
589 cal.setTime(df.parse(obj));
590 return cal;
591 } catch (ParseException e) {
592 throw new ConversionException(e);
593 }
594 }
595
596
597
598
599
600
601
602 public Calendar convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
603 DateFormat df = toDateTimeFormat(formatString == null ? CALENDAR_FORMAT : formatString, timeZone, locale);
604 try {
605 Date date = df.parse(obj);
606 Calendar cal = Calendar.getInstance(timeZone, locale);
607 cal.setTimeInMillis(date.getTime());
608 return cal;
609 } catch (ParseException e) {
610 throw new ConversionException(e);
611 }
612 }
613 }
614
615
616
617
618
619 public static class StringToDate extends AbstractLocalizedConverter<String, Date> {
620 public StringToDate() {
621 super(String.class, Date.class);
622 }
623
624 @Override
625 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
626 return String.class.equals(sourceClass) && Date.class.equals(targetClass);
627 }
628
629
630
631
632
633
634 public Date convert(String obj) throws ConversionException {
635 try {
636 DateFormat df = new SimpleDateFormat(CALENDAR_FORMAT);
637 return df.parse(obj);
638 } catch (ParseException e) {
639 throw new ConversionException(e);
640 }
641 }
642
643
644
645
646
647
648 public Date convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
649 DateFormat df = toDateTimeFormat(formatString == null ? CALENDAR_FORMAT : formatString, timeZone, locale);
650 try {
651 return df.parse(obj);
652 } catch (ParseException e) {
653 throw new ConversionException(e);
654 }
655 }
656 }
657
658
659
660
661
662 public static class StringToSqlDate extends AbstractLocalizedConverter<String, java.sql.Date> {
663 public StringToSqlDate() {
664 super(String.class, java.sql.Date.class);
665 }
666
667 @Override
668 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
669 return String.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass);
670 }
671
672 public java.sql.Date convert(String obj) throws ConversionException {
673 return java.sql.Date.valueOf(obj);
674 }
675
676
677
678
679
680
681
682 public java.sql.Date convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
683 DateFormat df = toDateFormat(timeZone);
684 try {
685 return new java.sql.Date(df.parse(obj).getTime());
686 } catch (ParseException e) {
687 throw new ConversionException(e);
688 }
689 }
690 }
691
692
693
694
695
696 public static class StringToSqlTime extends AbstractLocalizedConverter<String, java.sql.Time> {
697 public StringToSqlTime() {
698 super(String.class, java.sql.Time.class);
699 }
700
701 @Override
702 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
703 return String.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass);
704 }
705
706 public java.sql.Time convert(String obj) throws ConversionException {
707 return java.sql.Time.valueOf(obj);
708 }
709
710
711
712
713
714
715
716 public Time convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
717 DateFormat df = toTimeFormat(timeZone);
718 try {
719 return new java.sql.Time(df.parse(obj).getTime());
720 } catch (ParseException e) {
721 throw new ConversionException(e);
722 }
723 }
724 }
725
726
727
728
729
730 public static class StringToTimestamp extends AbstractLocalizedConverter<String, java.sql.Timestamp> {
731 public StringToTimestamp() {
732 super(String.class, java.sql.Timestamp.class);
733 }
734
735 @Override
736 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
737 return String.class.equals(sourceClass) && java.sql.Timestamp.class.equals(targetClass);
738 }
739
740 public Timestamp convert(String obj) throws ConversionException {
741 return java.sql.Timestamp.valueOf(obj);
742 }
743
744
745
746
747
748
749
750
751
752 public java.sql.Timestamp convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
753 try {
754
755
756
757 Timestamp parsedStamp = Timestamp.valueOf(obj);
758 Calendar cal = Calendar.getInstance(timeZone, locale);
759 cal.setTime(parsedStamp);
760 cal.add(Calendar.MILLISECOND, 0 - timeZone.getOffset(parsedStamp.getTime()));
761 Timestamp result = new Timestamp(cal.getTimeInMillis());
762 result.setNanos(parsedStamp.getNanos());
763 return result;
764 } catch (Exception e) {
765 throw new ConversionException(e);
766 }
767 }
768 }
769
770
771
772
773
774 public static class StringToTimeZone extends AbstractConverter<String, TimeZone> {
775 public StringToTimeZone() {
776 super(String.class, TimeZone.class);
777 }
778
779 public TimeZone convert(String obj) throws ConversionException {
780 return TimeZone.getTimeZone(obj);
781 }
782 }
783
784
785
786
787
788 public static class TimestampToDate extends AbstractConverter<java.sql.Timestamp, Date> {
789 public TimestampToDate() {
790 super(java.sql.Timestamp.class, Date.class);
791 }
792
793 @Override
794 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
795 return java.sql.Timestamp.class.equals(sourceClass) && java.util.Date.class.equals(targetClass);
796 }
797
798 public Date convert(java.sql.Timestamp obj) throws ConversionException {
799 return new Timestamp(obj.getTime());
800 }
801 }
802
803
804
805
806
807 public static class TimestampToSqlDate extends AbstractConverter<java.sql.Timestamp, java.sql.Date> {
808 public TimestampToSqlDate() {
809 super(java.sql.Timestamp.class, java.sql.Date.class);
810 }
811
812 @Override
813 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
814 return java.sql.Timestamp.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass);
815 }
816
817 public java.sql.Date convert(java.sql.Timestamp obj) throws ConversionException {
818 return new java.sql.Date(obj.getTime());
819 }
820 }
821
822
823
824
825
826 public static class TimestampToSqlTime extends AbstractConverter<java.sql.Timestamp, java.sql.Time> {
827 public TimestampToSqlTime() {
828 super(java.sql.Timestamp.class, java.sql.Time.class);
829 }
830
831 @Override
832 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
833 return java.sql.Timestamp.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass);
834 }
835
836 public java.sql.Time convert(java.sql.Timestamp obj) throws ConversionException {
837 return new java.sql.Time(obj.getTime());
838 }
839 }
840
841
842
843
844
845 public static class TimestampToString extends AbstractLocalizedConverter<java.sql.Timestamp, String> {
846 public TimestampToString() {
847 super(java.sql.Timestamp.class, String.class);
848 }
849
850 @Override
851 public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
852 return java.sql.Timestamp.class.equals(sourceClass) && String.class.equals(targetClass);
853 }
854
855 public String convert(java.sql.Timestamp obj) throws ConversionException {
856 return obj.toString();
857 }
858
859
860
861
862
863
864
865
866
867
868 public String convert(Timestamp obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
869 try {
870
871
872
873 Calendar cal = Calendar.getInstance(timeZone, locale);
874 cal.setTime(obj);
875 cal.add(Calendar.MILLISECOND, timeZone.getOffset(obj.getTime()));
876 Timestamp result = new Timestamp(cal.getTimeInMillis());
877 result.setNanos(obj.getNanos());
878 return result.toString();
879 } catch (Exception e) {
880 throw new ConversionException(e);
881 }
882 }
883 }
884
885
886
887
888
889 public static class TimeZoneToString extends AbstractConverter<TimeZone, String> {
890 public TimeZoneToString() {
891 super(TimeZone.class, String.class);
892 }
893
894 public String convert(TimeZone obj) throws ConversionException {
895 return obj.getID();
896 }
897 }
898 }