1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 *******************************************************************************
5 * Copyright (C) 1996-2014, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
8 *
9 */
10
11package com.ibm.icu.dev.test.serializable;
12
13import java.io.ByteArrayInputStream;
14import java.io.ByteArrayOutputStream;
15import java.io.File;
16import java.io.FileInputStream;
17import java.io.FileOutputStream;
18import java.io.IOException;
19import java.io.InputStream;
20import java.io.ObjectInputStream;
21import java.io.ObjectOutputStream;
22import java.lang.reflect.Modifier;
23import java.net.URL;
24import java.util.ArrayList;
25import java.util.Date;
26import java.util.Enumeration;
27import java.util.HashMap;
28import java.util.List;
29import java.util.Locale;
30
31import com.ibm.icu.dev.test.format.MeasureUnitTest;
32import com.ibm.icu.dev.test.format.PluralRulesTest;
33import com.ibm.icu.impl.JavaTimeZone;
34import com.ibm.icu.impl.OlsonTimeZone;
35import com.ibm.icu.impl.TimeZoneAdapter;
36import com.ibm.icu.impl.URLHandler;
37import com.ibm.icu.impl.Utility;
38import com.ibm.icu.math.BigDecimal;
39import com.ibm.icu.math.MathContext;
40import com.ibm.icu.util.AnnualTimeZoneRule;
41import com.ibm.icu.util.Calendar;
42import com.ibm.icu.util.Currency;
43import com.ibm.icu.util.DateInterval;
44import com.ibm.icu.util.DateTimeRule;
45import com.ibm.icu.util.GregorianCalendar;
46import com.ibm.icu.util.ICUCloneNotSupportedException;
47import com.ibm.icu.util.ICUException;
48import com.ibm.icu.util.ICUUncheckedIOException;
49import com.ibm.icu.util.InitialTimeZoneRule;
50import com.ibm.icu.util.RuleBasedTimeZone;
51import com.ibm.icu.util.SimpleTimeZone;
52import com.ibm.icu.util.TimeArrayTimeZoneRule;
53import com.ibm.icu.util.TimeZone;
54import com.ibm.icu.util.TimeZoneRule;
55import com.ibm.icu.util.ULocale;
56import com.ibm.icu.util.VTimeZone;
57
58/**
59 * @author emader
60 *
61 * TODO To change the template for this generated type comment go to
62 * Window - Preferences - Java - Code Style - Code Templates
63 */
64public class SerializableTestUtility {
65    private static Class serializable;
66    static {
67        try {
68            serializable = Class.forName("java.io.Serializable");
69        } catch (ClassNotFoundException e) {
70            // TODO Auto-generated catch block
71            e.printStackTrace();
72        }
73    }
74
75    public interface Handler
76    {
77        public Object[] getTestObjects();
78
79        public boolean hasSameBehavior(Object a, Object b);
80    }
81
82    public static Handler getHandler(String className)
83    {
84        return (Handler) map.get(className);
85    }
86
87    private static class TimeZoneHandler implements Handler
88    {
89        String[] ZONES = { "GMT", "MET", "IST" };
90
91        @Override
92        public Object[] getTestObjects()
93        {
94            TimeZone zones[] = new TimeZone[ZONES.length];
95
96            for(int z = 0; z < ZONES.length; z += 1) {
97                zones[z] = TimeZone.getTimeZone(ZONES[z]);
98            }
99
100            return zones;
101        }
102
103        @Override
104        public boolean hasSameBehavior(Object a, Object b)
105        {
106            TimeZone zone_a = (TimeZone) a;
107            TimeZone zone_b = (TimeZone) b;
108
109            if (!(zone_a.getDisplayName().equals(zone_b.getDisplayName()))) {
110                return false;
111            }
112
113            int a_offsets[] = {0, 0};
114            int b_offsets[] = {0, 0};
115
116            boolean bSame = true;
117            for (int i = 0; i < sampleTimes.length; i++) {
118                zone_a.getOffset(sampleTimes[i], false, a_offsets);
119                zone_b.getOffset(sampleTimes[i], false, b_offsets);
120                if (a_offsets[0] != b_offsets[0] || a_offsets[1] != b_offsets[1]) {
121                    bSame = false;
122                    break;
123                }
124            }
125            return bSame;
126        }
127    }
128
129    private static Locale locales[] = {
130        Locale.CANADA, Locale.CANADA_FRENCH, Locale.CHINA,
131        Locale.CHINESE, Locale.ENGLISH, Locale.FRANCE, Locale.FRENCH,
132        Locale.GERMAN, Locale.GERMANY, Locale.ITALIAN, Locale.ITALY,
133        Locale.JAPAN, Locale.JAPANESE, Locale.KOREA, Locale.KOREAN,
134        Locale.PRC, Locale.SIMPLIFIED_CHINESE, Locale.TAIWAN,
135        Locale.TRADITIONAL_CHINESE, Locale.UK, Locale.US
136    };
137
138    private static Locale places[] = {
139        Locale.CANADA, Locale.CANADA_FRENCH, Locale.CHINA,
140        Locale.FRANCE, Locale.GERMANY, Locale.ITALY,
141        Locale.JAPAN, Locale.KOREA, Locale.PRC, Locale.TAIWAN,
142        Locale.UK, Locale.US
143    };
144
145    public static Locale[] getLocales()
146    {
147        return locales;
148    }
149
150    public static boolean compareStrings(String a[], String b[])
151    {
152        if (a.length != b.length) {
153            return false;
154        }
155
156        for (int i = 0; i < a.length; i += 1) {
157            if (! a[i].equals(b[i])) {
158                return false;
159            }
160        }
161
162        return true;
163    }
164
165    public static boolean compareChars(char a[], char b[])
166    {
167        if (a.length != b.length) {
168            return false;
169        }
170
171        for (int i = 0; i < a.length; i += 1) {
172            if (a[i] != b[i]) {
173                return false;
174            }
175        }
176
177        return true;
178    }
179
180    private static class SimpleTimeZoneHandler extends TimeZoneHandler
181    {
182        @Override
183        public Object[] getTestObjects()
184        {
185            SimpleTimeZone simpleTimeZones[] = new SimpleTimeZone[6];
186
187            simpleTimeZones[0] = new SimpleTimeZone(32400000, "MyTimeZone");
188
189            simpleTimeZones[1] = new SimpleTimeZone(32400000, "Asia/Tokyo");
190
191            simpleTimeZones[2] = new SimpleTimeZone(32400000, "Asia/Tokyo");
192            simpleTimeZones[2].setRawOffset(0);
193
194            simpleTimeZones[3] = new SimpleTimeZone(32400000, "Asia/Tokyo");
195            simpleTimeZones[3].setStartYear(100);
196
197            simpleTimeZones[4] = new SimpleTimeZone(32400000, "Asia/Tokyo");
198            simpleTimeZones[4].setStartYear(1000);
199            simpleTimeZones[4].setDSTSavings(1800000);
200            simpleTimeZones[4].setStartRule(3, 4, 180000);
201            simpleTimeZones[4].setEndRule(6, 3, 4, 360000);
202
203            simpleTimeZones[5] = new SimpleTimeZone(32400000, "Asia/Tokyo");
204            simpleTimeZones[5].setStartRule(2, 3, 4, 360000);
205            simpleTimeZones[5].setEndRule(6, 3, 4, 360000);
206
207            return simpleTimeZones;
208        }
209    }
210
211    private static class VTimeZoneHandler extends TimeZoneHandler {
212        @Override
213        public Object[] getTestObjects() {
214            //TODO
215            VTimeZone[] vtzs = new VTimeZone[1];
216            vtzs[0] = VTimeZone.create("America/New_York");
217            return vtzs;
218        }
219    }
220
221    private static final int HOUR = 60*60*1000;
222    private static final AnnualTimeZoneRule[] TEST_US_EASTERN = {
223        new AnnualTimeZoneRule("EST", -5*HOUR, 0,
224                new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*HOUR, DateTimeRule.WALL_TIME),
225                1967, 2006),
226
227        new AnnualTimeZoneRule("EST", -5*HOUR, 0,
228                new DateTimeRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, true, 2*HOUR, DateTimeRule.WALL_TIME),
229                2007, AnnualTimeZoneRule.MAX_YEAR),
230
231        new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR,
232                new DateTimeRule(Calendar.APRIL, -1, Calendar.SUNDAY, 2*HOUR, DateTimeRule.WALL_TIME),
233                1967, 1973),
234
235        new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR,
236                new DateTimeRule(Calendar.JANUARY, 6, 2*HOUR, DateTimeRule.WALL_TIME),
237                1974, 1974),
238
239        new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR,
240                new DateTimeRule(Calendar.FEBRUARY, 23, 2*HOUR, DateTimeRule.WALL_TIME),
241                1975, 1975),
242
243        new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR,
244                new DateTimeRule(Calendar.APRIL, -1, Calendar.SUNDAY, 2*HOUR, DateTimeRule.WALL_TIME),
245                1976, 1986),
246
247        new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR,
248                new DateTimeRule(Calendar.APRIL, 1, Calendar.SUNDAY, true, 2*HOUR, DateTimeRule.WALL_TIME),
249                1987, 2006),
250
251        new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR,
252                new DateTimeRule(Calendar.MARCH, 8, Calendar.SUNDAY, true, 2*HOUR, DateTimeRule.WALL_TIME),
253                2007, AnnualTimeZoneRule.MAX_YEAR)
254    };
255
256    private static class RuleBasedTimeZoneHandler extends TimeZoneHandler
257    {
258        @Override
259        public Object[] getTestObjects()
260        {
261            RuleBasedTimeZone ruleBasedTimeZones[] = new RuleBasedTimeZone[2];
262
263            InitialTimeZoneRule ir = new InitialTimeZoneRule("GMT-5", -5*HOUR, 0);
264
265            // GMT-5, no transition
266            ruleBasedTimeZones[0] = new RuleBasedTimeZone("GMT-5", ir);
267
268
269            // US Eastern since 1967
270            ruleBasedTimeZones[1] = new RuleBasedTimeZone("US_East", ir);
271            for (int i = 0; i < TEST_US_EASTERN.length; i++) {
272                ruleBasedTimeZones[1].addTransitionRule(TEST_US_EASTERN[i]);
273            }
274            return ruleBasedTimeZones;
275        }
276    }
277
278    private static class DateTimeRuleHandler implements Handler {
279        @Override
280        public Object[] getTestObjects() {
281            DateTimeRule[] rules = new DateTimeRule[4];
282
283            // DOM + UTC
284            rules[0] = new DateTimeRule(Calendar.OCTOBER, 10, 13*HOUR, DateTimeRule.UTC_TIME);
285
286            // DOW + WALL
287            rules[1] = new DateTimeRule(Calendar.MARCH, 2, Calendar.SUNDAY, 2*HOUR, DateTimeRule.WALL_TIME);
288
289            // DOW_GEQ_DOM + STD
290            rules[2] = new DateTimeRule(Calendar.MAY, 1, Calendar.MONDAY, true, 0*HOUR, DateTimeRule.STANDARD_TIME);
291
292            // DOW_LEQ_DOM + WALL
293            rules[3] = new DateTimeRule(Calendar.AUGUST, 31, Calendar.SATURDAY, false, 1*HOUR, DateTimeRule.WALL_TIME);
294
295            return rules;
296        }
297
298        @Override
299        public boolean hasSameBehavior(Object a, Object b) {
300            return hasSameRule((DateTimeRule)a, (DateTimeRule)b);
301        }
302
303        static boolean hasSameRule(DateTimeRule dtra, DateTimeRule dtrb) {
304            boolean bSame = false;
305            if (dtra.getDateRuleType() == dtrb.getDateRuleType()
306                    && dtra.getRuleMonth() == dtrb.getRuleMonth()
307                    && dtra.getTimeRuleType() == dtrb.getTimeRuleType()
308                    && dtra.getRuleMillisInDay() == dtrb.getRuleMillisInDay()) {
309                switch (dtra.getDateRuleType()) {
310                case DateTimeRule.DOM:
311                    bSame = (dtra.getRuleDayOfMonth() == dtrb.getRuleDayOfMonth());
312                    break;
313                case DateTimeRule.DOW:
314                    bSame = (dtra.getRuleDayOfWeek() == dtrb.getRuleDayOfWeek() &&
315                                dtra.getRuleWeekInMonth() == dtrb.getRuleWeekInMonth());
316                    break;
317                case DateTimeRule.DOW_GEQ_DOM:
318                case DateTimeRule.DOW_LEQ_DOM:
319                    bSame = (dtra.getRuleDayOfMonth() == dtrb.getRuleDayOfMonth() &&
320                                dtra.getRuleDayOfWeek() == dtrb.getRuleDayOfWeek());
321                    break;
322                }
323            }
324            return bSame;
325        }
326    }
327
328    private static boolean compareTimeZoneRules(TimeZoneRule ra, TimeZoneRule rb) {
329        if (ra.getName().equals(rb.getName()) &&
330                ra.getRawOffset() == rb.getRawOffset() &&
331                ra.getDSTSavings() == rb.getDSTSavings()) {
332            return true;
333        }
334        return false;
335    }
336
337    private static class AnnualTimeZoneRuleHandler implements Handler {
338        @Override
339        public Object[] getTestObjects() {
340            return TEST_US_EASTERN;
341        }
342
343        @Override
344        public boolean hasSameBehavior(Object a, Object b) {
345            AnnualTimeZoneRule ra = (AnnualTimeZoneRule)a;
346            AnnualTimeZoneRule rb = (AnnualTimeZoneRule)b;
347            if (DateTimeRuleHandler.hasSameRule(ra.getRule(), rb.getRule()) &&
348                    ra.getStartYear() == rb.getStartYear() &&
349                    ra.getEndYear() == rb.getEndYear()) {
350                return compareTimeZoneRules(ra, rb);
351            }
352            return false;
353        }
354    }
355
356    private static class InitialTimeZoneRuleHandler implements Handler {
357        @Override
358        public Object[] getTestObjects() {
359            TimeZoneRule[] rules = new TimeZoneRule[2];
360            rules[0] = new InitialTimeZoneRule("EST", -5*HOUR, 0);
361            rules[1] = new InitialTimeZoneRule("PST", -8*HOUR, 0);
362            return rules;
363        }
364
365        @Override
366        public boolean hasSameBehavior(Object a, Object b) {
367            return compareTimeZoneRules((TimeZoneRule)a, (TimeZoneRule)b);
368        }
369    }
370
371    private static class TimeArrayTimeZoneRuleHandler implements Handler {
372        @Override
373        public Object[] getTestObjects() {
374            TimeArrayTimeZoneRule[] rules = new TimeArrayTimeZoneRule[1];
375            long[] ttime = new long[] {-631152000000L, 0L, 946684800000L}; /* {1950-1-1, 1970-1-1, 2000-1-1} */
376            rules[0] = new TimeArrayTimeZoneRule("Foo", 1*HOUR, 1*HOUR, ttime, DateTimeRule.UTC_TIME);
377
378            return rules;
379        }
380        @Override
381        public boolean hasSameBehavior(Object a, Object b) {
382            TimeArrayTimeZoneRule ra = (TimeArrayTimeZoneRule)a;
383            TimeArrayTimeZoneRule rb = (TimeArrayTimeZoneRule)b;
384
385            Date da = ra.getFirstStart(0, 0);
386            Date db = rb.getFirstStart(0, 0);
387            long t = da.getTime();
388            if (da.equals(db)) {
389                da = ra.getFinalStart(0, 0);
390                db = rb.getFinalStart(0, 0);
391                long end = da.getTime();
392                if (da.equals(db)) {
393                    while (t < end) {
394                        da = ra.getNextStart(t, 0, 0, false);
395                        db = ra.getNextStart(t, 0, 0, false);
396                        if (da == null || db == null || !da.equals(db)) {
397                            break;
398                        }
399                        t = da.getTime();
400                    }
401                    return compareTimeZoneRules(ra, rb);
402                }
403            }
404            return false;
405        }
406    }
407
408    private static class ULocaleHandler implements Handler
409    {
410        @Override
411        public Object[] getTestObjects()
412        {
413            ULocale uLocales[] = new ULocale[locales.length];
414
415            for (int i = 0; i < locales.length; i += 1) {
416                uLocales[i] = ULocale.forLocale(locales[i]);
417            }
418
419            return uLocales;
420        }
421
422        @Override
423        public boolean hasSameBehavior(Object a, Object b)
424        {
425            ULocale uloc_a = (ULocale) a;
426            ULocale uloc_b = (ULocale) b;
427
428            return uloc_a.getName().equals(uloc_b.getName());
429        }
430    }
431
432    public static class DateIntervalHandler implements Handler
433    {
434        private DateInterval dateInterval[] = {
435                new DateInterval(0L, 1164931200000L/*20061201T000000Z*/)
436        };
437        @Override
438        public Object[] getTestObjects()
439        {
440            return dateInterval;
441        }
442
443        @Override
444        public boolean hasSameBehavior(Object a, Object b)
445        {
446            return a.equals(b);
447        }
448    }
449
450    private static class CurrencyHandler implements Handler
451    {
452        @Override
453        public Object[] getTestObjects()
454        {
455            Currency currencies[] = new Currency[places.length];
456
457            for (int i = 0; i < places.length; i += 1) {
458                currencies[i] = Currency.getInstance(places[i]);
459            }
460
461            return currencies;
462        }
463
464        @Override
465        public boolean hasSameBehavior(Object a, Object b)
466        {
467
468            Currency curr_a = (Currency) a;
469            Currency curr_b = (Currency) b;
470
471            return a == b
472                    || a != null && b != null
473                    && curr_a.getCurrencyCode() != null
474                    && curr_a.getCurrencyCode().equals(curr_b.getCurrencyCode());
475
476        }
477    }
478
479    private static String zoneIDs[] = {
480        "Pacific/Honolulu", "America/Anchorage", "America/Los_Angeles", "America/Denver",
481        "America/Chicago", "America/New_York", "Africa/Cairo", "Africa/Addis_Ababa", "Africa/Dar_es_Salaam",
482        "Africa/Freetown", "Africa/Johannesburg", "Africa/Nairobi", "Asia/Bangkok", "Asia/Baghdad",
483        "Asia/Calcutta", "Asia/Hong_Kong", "Asia/Jakarta", "Asia/Jerusalem", "Asia/Manila", "Asia/Tokyo",
484        "Europe/Amsterdam", "Europe/Athens", "Europe/Berlin", "Europe/London", "Europe/Malta", "Europe/Moscow",
485        "Europe/Paris", "Europe/Rome"
486    };
487
488    private static long sampleTimes[] = {
489        1136073600000L, // 20060101T000000Z
490        1138752000000L, // 20060201T000000Z
491        1141171200000L, // 20060301T000000Z
492        1143849600000L, // 20060401T000000Z
493        1146441600000L, // 20060501T000000Z
494        1149120000000L, // 20060601T000000Z
495        1151712000000L, // 20060701T000000Z
496        1154390400000L, // 20060801T000000Z
497        1157068800000L, // 20060901T000000Z
498        1159660800000L, // 20061001T000000Z
499        1162339200000L, // 20061101T000000Z
500        1164931200000L, // 20061201T000000Z
501    };
502
503    private static class OlsonTimeZoneHandler implements Handler
504    {
505        @Override
506        public Object[] getTestObjects()
507        {
508            OlsonTimeZone timeZones[] = new OlsonTimeZone[zoneIDs.length];
509
510            for (int i = 0; i < zoneIDs.length; i += 1) {
511                timeZones[i] = new OlsonTimeZone(zoneIDs[i]);
512            }
513
514            return timeZones;
515
516        }
517
518        @Override
519        public boolean hasSameBehavior(Object a, Object b)
520        {
521            OlsonTimeZone otz_a = (OlsonTimeZone) a;
522            OlsonTimeZone otz_b = (OlsonTimeZone) b;
523            int a_offsets[] = {0, 0};
524            int b_offsets[] = {0, 0};
525
526            boolean bSame = true;
527            for (int i = 0; i < sampleTimes.length; i++) {
528                otz_a.getOffset(sampleTimes[i], false, a_offsets);
529                otz_b.getOffset(sampleTimes[i], false, b_offsets);
530                if (a_offsets[0] != b_offsets[0] || a_offsets[1] != b_offsets[1]) {
531                    bSame = false;
532                    break;
533                }
534            }
535            return bSame;
536        }
537    }
538
539    private static class TimeZoneAdapterHandler implements Handler
540    {
541        @Override
542        public Object[] getTestObjects()
543        {
544            TimeZoneAdapter timeZones[] = new TimeZoneAdapter[zoneIDs.length];
545
546            for (int i = 0; i < zoneIDs.length; i += 1) {
547                timeZones[i] = new TimeZoneAdapter(TimeZone.getTimeZone(zoneIDs[i]));
548            }
549
550            return timeZones;
551
552        }
553
554        @Override
555        public boolean hasSameBehavior(Object a, Object b)
556        {
557            GregorianCalendar cal = new GregorianCalendar();
558            TimeZoneAdapter tza_a = (TimeZoneAdapter) a;
559            TimeZoneAdapter tza_b = (TimeZoneAdapter) b;
560
561            int a_offset, b_offset;
562            boolean a_dst, b_dst;
563            boolean bSame = true;
564            for (int i = 0; i < sampleTimes.length; i++) {
565                cal.setTimeInMillis(sampleTimes[i]);
566                int era = cal.get(Calendar.ERA);
567                int year = cal.get(Calendar.YEAR);
568                int month = cal.get(Calendar.MONTH);
569                int day = cal.get(Calendar.DAY_OF_MONTH);
570                int dow = cal.get(Calendar.DAY_OF_WEEK);
571                int mid = cal.get(Calendar.MILLISECONDS_IN_DAY);
572                a_offset = tza_a.getOffset(era, year, month, day, dow, mid);
573                b_offset = tza_b.getOffset(era, year, month, day, dow, mid);
574                Date d = new Date(sampleTimes[i]);
575                a_dst = tza_a.inDaylightTime(d);
576                b_dst = tza_b.inDaylightTime(d);
577                if (a_offset != b_offset || a_dst != b_dst) {
578                    bSame = false;
579                    break;
580                }
581            }
582            return bSame;
583        }
584    }
585
586    private static class JavaTimeZoneHandler implements Handler {
587        String[] ZONES = { "GMT", "America/New_York", "GMT+05:45" };
588
589        @Override
590        public Object[] getTestObjects() {
591            JavaTimeZone zones[] = new JavaTimeZone[ZONES.length];
592            for(int z = 0; z < ZONES.length; z += 1) {
593                java.util.TimeZone tz = java.util.TimeZone.getTimeZone(ZONES[z]);
594                zones[z] = new JavaTimeZone(tz, ZONES[z]);
595            }
596            return zones;
597        }
598
599        @Override
600        public boolean hasSameBehavior(Object a, Object b)
601        {
602            TimeZone zone_a = (TimeZone) a;
603            TimeZone zone_b = (TimeZone) b;
604
605            if (!(TimeZone.getCanonicalID(zone_a.getID()).equals(TimeZone.getCanonicalID(zone_b.getID())))) {
606                return false;
607            }
608
609            int a_offsets[] = {0, 0};
610            int b_offsets[] = {0, 0};
611
612            boolean bSame = true;
613            for (int i = 0; i < sampleTimes.length; i++) {
614                zone_a.getOffset(sampleTimes[i], false, a_offsets);
615                zone_b.getOffset(sampleTimes[i], false, b_offsets);
616                if (a_offsets[0] != b_offsets[0] || a_offsets[1] != b_offsets[1]) {
617                    bSame = false;
618                    break;
619                }
620            }
621            return bSame;
622        }
623    }
624
625    private static class BigDecimalHandler implements Handler
626    {
627        String values[] = {
628            "1234567890.",
629            "123456789.0",
630            "12345678.90",
631            "1234567.890",
632            "123456.7890",
633            "12345.67890",
634            "1234.567890",
635            "123.4567890",
636            "12.34567890",
637            "1.234567890",
638            ".1234567890"};
639
640        @Override
641        public Object[] getTestObjects()
642        {
643            BigDecimal bds[] = new BigDecimal[values.length];
644
645            for (int i = 0; i < values.length; i += 1) {
646                bds[i] = new BigDecimal(values[i]);
647            }
648
649            return bds;
650        }
651
652        @Override
653        public boolean hasSameBehavior(Object a, Object b) {
654            BigDecimal bda = (BigDecimal) a;
655            BigDecimal bdb = (BigDecimal) b;
656
657            return bda.toString().equals(bdb.toString());
658        }
659    }
660
661    private static class MathContextHandler implements Handler
662    {
663        int forms[] = {MathContext.PLAIN, MathContext.ENGINEERING, MathContext.SCIENTIFIC};
664        int rounds[] = {
665            MathContext.ROUND_CEILING, MathContext.ROUND_DOWN, MathContext.ROUND_FLOOR,
666            MathContext.ROUND_HALF_DOWN, MathContext.ROUND_HALF_EVEN, MathContext.ROUND_HALF_UP,
667            MathContext.ROUND_UNNECESSARY, MathContext.ROUND_UP};
668
669        @Override
670        public Object[] getTestObjects()
671        {
672            int objectCount = forms.length * rounds.length;
673            MathContext contexts[] = new MathContext[objectCount];
674            int i = 0;
675
676            for (int f = 0; f < forms.length; f += 1) {
677                for (int r = 0; r < rounds.length; r += 1) {
678                    int digits = f * r;
679                    boolean lostDigits = (r & 1) != 0;
680
681                    contexts[i++] = new MathContext(digits, forms[f], lostDigits, rounds[r]);
682                }
683            }
684
685            return contexts;
686        }
687
688        @Override
689        public boolean hasSameBehavior(Object a, Object b)
690        {
691            MathContext mca = (MathContext) a;
692            MathContext mcb = (MathContext) b;
693
694            return mca.toString().equals(mcb.toString());
695        }
696    }
697
698    private static abstract class ExceptionHandlerBase implements Handler {
699        @Override
700        public boolean hasSameBehavior(Object a, Object b) {
701            return sameThrowable((Exception) a, (Exception) b);
702        }
703
704        // Exception.equals() does not seem to work.
705        private static final boolean sameThrowable(Throwable a, Throwable b) {
706            return a == null ? b == null :
707                    b == null ? false :
708                            a.getClass().equals(b.getClass()) &&
709                            Utility.objectEquals(a.getMessage(), b.getMessage()) &&
710                            sameThrowable(a.getCause(), b.getCause());
711        }
712    }
713
714    private static class ICUExceptionHandler extends ExceptionHandlerBase {
715        @Override
716        public Object[] getTestObjects() {
717            return new ICUException[] {
718                    new ICUException(),
719                    new ICUException("msg1"),
720                    new ICUException(new RuntimeException("rte1")),
721                    new ICUException("msg2", new RuntimeException("rte2"))
722            };
723        }
724    }
725
726    private static class ICUUncheckedIOExceptionHandler extends ExceptionHandlerBase {
727        @Override
728        public Object[] getTestObjects() {
729            return new ICUUncheckedIOException[] {
730                    new ICUUncheckedIOException(),
731                    new ICUUncheckedIOException("msg1"),
732                    new ICUUncheckedIOException(new RuntimeException("rte1")),
733                    new ICUUncheckedIOException("msg2", new RuntimeException("rte2"))
734            };
735        }
736    }
737
738    private static class ICUCloneNotSupportedExceptionHandler extends ExceptionHandlerBase {
739        @Override
740        public Object[] getTestObjects() {
741            return new ICUCloneNotSupportedException[] {
742                    new ICUCloneNotSupportedException(),
743                    new ICUCloneNotSupportedException("msg1"),
744                    new ICUCloneNotSupportedException(new RuntimeException("rte1")),
745                    new ICUCloneNotSupportedException("msg2", new RuntimeException("rte2"))
746            };
747        }
748    }
749
750    private static HashMap map = new HashMap();
751
752    static {
753        map.put("com.ibm.icu.util.TimeZone", new TimeZoneHandler());
754        map.put("com.ibm.icu.util.SimpleTimeZone", new SimpleTimeZoneHandler());
755        map.put("com.ibm.icu.util.RuleBasedTimeZone", new RuleBasedTimeZoneHandler());
756        map.put("com.ibm.icu.util.VTimeZone", new VTimeZoneHandler());
757        map.put("com.ibm.icu.util.DateTimeRule", new DateTimeRuleHandler());
758        map.put("com.ibm.icu.util.AnnualTimeZoneRule", new AnnualTimeZoneRuleHandler());
759        map.put("com.ibm.icu.util.InitialTimeZoneRule", new InitialTimeZoneRuleHandler());
760        map.put("com.ibm.icu.util.TimeArrayTimeZoneRule", new TimeArrayTimeZoneRuleHandler());
761        map.put("com.ibm.icu.util.ULocale", new ULocaleHandler());
762        map.put("com.ibm.icu.util.Currency", new CurrencyHandler());
763        map.put("com.ibm.icu.impl.JavaTimeZone", new JavaTimeZoneHandler());
764        map.put("com.ibm.icu.impl.OlsonTimeZone", new OlsonTimeZoneHandler());
765        map.put("com.ibm.icu.impl.TimeZoneAdapter", new TimeZoneAdapterHandler());
766        map.put("com.ibm.icu.math.BigDecimal", new BigDecimalHandler());
767        map.put("com.ibm.icu.math.MathContext", new MathContextHandler());
768
769        map.put("com.ibm.icu.text.NumberFormat", new FormatHandler.NumberFormatHandler());
770        map.put("com.ibm.icu.text.DecimalFormat", new FormatHandler.DecimalFormatHandler());
771        map.put("com.ibm.icu.text.CompactDecimalFormat", new FormatHandler.CompactDecimalFormatHandler());
772        map.put("com.ibm.icu.text.RuleBasedNumberFormat", new FormatHandler.RuleBasedNumberFormatHandler());
773        map.put("com.ibm.icu.text.CurrencyPluralInfo", new FormatHandler.CurrencyPluralInfoHandler());
774        map.put("com.ibm.icu.text.DecimalFormatSymbols", new FormatHandler.DecimalFormatSymbolsHandler());
775        map.put("com.ibm.icu.text.MessageFormat", new FormatHandler.MessageFormatHandler());
776        map.put("com.ibm.icu.text.DateFormat", new FormatHandler.DateFormatHandler());
777        map.put("com.ibm.icu.text.DateFormatSymbols", new FormatHandler.DateFormatSymbolsHandler());
778        map.put("com.ibm.icu.util.DateInterval", new DateIntervalHandler());
779        map.put("com.ibm.icu.text.DateIntervalFormat", new FormatHandler.DateIntervalFormatHandler());
780        map.put("com.ibm.icu.text.DateIntervalInfo", new FormatHandler.DateIntervalInfoHandler());
781        map.put("com.ibm.icu.text.DateIntervalInfo$PatternInfo", new FormatHandler.PatternInfoHandler());
782        map.put("com.ibm.icu.text.SimpleDateFormat", new FormatHandler.SimpleDateFormatHandler());
783        map.put("com.ibm.icu.text.ChineseDateFormat", new FormatHandler.ChineseDateFormatHandler());
784        map.put("com.ibm.icu.text.ChineseDateFormatSymbols", new FormatHandler.ChineseDateFormatSymbolsHandler());
785        map.put("com.ibm.icu.impl.DateNumberFormat", new FormatHandler.DateNumberFormatHandler());
786        map.put("com.ibm.icu.text.PluralFormat", new FormatHandler.PluralFormatHandler());
787        map.put("com.ibm.icu.text.PluralRules", new FormatHandler.PluralRulesHandler());
788        map.put("com.ibm.icu.text.PluralRulesSerialProxy", new FormatHandler.PluralRulesSerialProxyHandler());
789        map.put("com.ibm.icu.text.TimeUnitFormat", new FormatHandler.TimeUnitFormatHandler());
790        map.put("com.ibm.icu.text.SelectFormat", new FormatHandler.SelectFormatHandler());
791        map.put("com.ibm.icu.impl.TimeZoneNamesImpl", new FormatHandler.TimeZoneNamesHandler());
792        map.put("com.ibm.icu.text.TimeZoneFormat", new FormatHandler.TimeZoneFormatHandler());
793        map.put("com.ibm.icu.impl.TimeZoneGenericNames", new FormatHandler.TimeZoneGenericNamesHandler());
794        map.put("com.ibm.icu.impl.TZDBTimeZoneNames", new FormatHandler.TZDBTimeZoneNamesHandler());
795
796        map.put("com.ibm.icu.util.Calendar", new CalendarHandler.BasicCalendarHandler());
797        map.put("com.ibm.icu.util.BuddhistCalendar", new CalendarHandler.BuddhistCalendarHandler());
798        map.put("com.ibm.icu.util.ChineseCalendar", new CalendarHandler.ChineseCalendarHandler());
799        map.put("com.ibm.icu.util.CopticCalendar", new CalendarHandler.CopticCalendarHandler());
800        map.put("com.ibm.icu.util.DangiCalendar", new CalendarHandler.DangiCalendarHandler());
801        map.put("com.ibm.icu.util.EthiopicCalendar", new CalendarHandler.EthiopicCalendarHandler());
802        map.put("com.ibm.icu.util.GregorianCalendar", new CalendarHandler.GregorianCalendarHandler());
803        map.put("com.ibm.icu.util.HebrewCalendar", new CalendarHandler.HebrewCalendarHandler());
804        map.put("com.ibm.icu.util.IndianCalendar", new CalendarHandler.IndianCalendarHandler());
805        map.put("com.ibm.icu.util.IslamicCalendar", new CalendarHandler.IslamicCalendarHandler());
806        map.put("com.ibm.icu.util.JapaneseCalendar", new CalendarHandler.JapaneseCalendarHandler());
807        map.put("com.ibm.icu.util.PersianCalendar", new CalendarHandler.PersianCalendarHandler());
808        map.put("com.ibm.icu.util.TaiwanCalendar", new CalendarHandler.TaiwanCalendarHandler());
809
810        map.put("com.ibm.icu.text.ArabicShapingException", new ExceptionHandler.ArabicShapingExceptionHandler());
811        map.put("com.ibm.icu.text.StringPrepParseException", new ExceptionHandler.StringPrepParseExceptionHandler());
812        map.put("com.ibm.icu.util.UResourceTypeMismatchException", new ExceptionHandler.UResourceTypeMismatchExceptionHandler());
813        map.put("com.ibm.icu.impl.InvalidFormatException", new ExceptionHandler.InvalidFormatExceptionHandler());
814
815        map.put("com.ibm.icu.text.NumberFormat$Field", new FormatHandler.NumberFormatFieldHandler());
816        map.put("com.ibm.icu.text.DateFormat$Field", new FormatHandler.DateFormatFieldHandler());
817        map.put("com.ibm.icu.text.ChineseDateFormat$Field", new FormatHandler.ChineseDateFormatFieldHandler());
818        map.put("com.ibm.icu.text.MessageFormat$Field", new FormatHandler.MessageFormatFieldHandler());
819
820        map.put("com.ibm.icu.impl.duration.BasicDurationFormat", new FormatHandler.BasicDurationFormatHandler());
821        map.put("com.ibm.icu.impl.RelativeDateFormat", new FormatHandler.RelativeDateFormatHandler());
822        map.put("com.ibm.icu.util.IllformedLocaleException", new ExceptionHandler.IllformedLocaleExceptionHandler());
823        map.put("com.ibm.icu.impl.locale.LocaleSyntaxException", new ExceptionHandler.LocaleSyntaxExceptionHandler());
824        map.put("com.ibm.icu.impl.IllegalIcuArgumentException", new ExceptionHandler.IllegalIcuArgumentExceptionHandler());
825
826        map.put("com.ibm.icu.text.PluralRules$FixedDecimal", new PluralRulesTest.FixedDecimalHandler());
827        map.put("com.ibm.icu.util.MeasureUnit", new MeasureUnitTest.MeasureUnitHandler());
828        map.put("com.ibm.icu.util.TimeUnit", new MeasureUnitTest.MeasureUnitHandler());
829        map.put("com.ibm.icu.text.MeasureFormat", new MeasureUnitTest.MeasureFormatHandler());
830
831        map.put("com.ibm.icu.util.ICUException", new ICUExceptionHandler());
832        map.put("com.ibm.icu.util.ICUUncheckedIOException", new ICUUncheckedIOExceptionHandler());
833        map.put("com.ibm.icu.util.ICUCloneNotSupportedException", new ICUCloneNotSupportedExceptionHandler());
834    }
835
836    /*
837     * Serialization Helpers
838     */
839    static Object[] getSerializedObjects(byte[] serializedBytes) throws ClassNotFoundException, IOException {
840        ByteArrayInputStream bis = new ByteArrayInputStream(serializedBytes);
841        ObjectInputStream ois = new ObjectInputStream(bis);
842        Object inputObjects[] = (Object[]) ois.readObject();
843
844        ois.close();
845        return inputObjects;
846    }
847
848    static byte[] getSerializedBytes(Object[] objectsOut) throws IOException {
849        ByteArrayOutputStream bos = new ByteArrayOutputStream();
850        ObjectOutputStream oos = new ObjectOutputStream(bos);
851        oos.writeObject(objectsOut);
852
853        byte[] serializedBytes = bos.toByteArray();
854        oos.close();
855        return serializedBytes;
856    }
857
858    static Object[] getSerializedObjects(File testFile) throws IOException, ClassNotFoundException {
859        FileInputStream fis = new FileInputStream(testFile);
860        ObjectInputStream ois = new ObjectInputStream(fis);
861        Object[] objects = (Object[]) ois.readObject();
862        fis.close();
863        return objects;
864    }
865
866    static byte[] copyStreamBytes(InputStream is) throws IOException {
867        byte[] buffer = new byte[1024];
868
869        ByteArrayOutputStream bos = new ByteArrayOutputStream();
870        int len;
871        while((len = is.read(buffer, 0, buffer.length)) >= 0) {
872            bos.write(buffer, 0, len);
873        }
874        return bos.toByteArray();
875    }
876
877    static List<String> getSerializationClassList(Object caller) throws IOException {
878        List<String> classList = new ArrayList();
879        Enumeration<URL> urlEnum = caller.getClass().getClassLoader().getResources("com/ibm/icu");
880        while (urlEnum.hasMoreElements()) {
881            URL url = urlEnum.nextElement();
882            URLHandler handler  = URLHandler.get(url);
883            if (handler == null) {
884                System.out.println("Unsupported URL: " + url);
885                continue;
886            }
887            CoverageClassVisitor visitor = new CoverageClassVisitor(classList);
888            handler.guide(visitor, true, false);
889        }
890        return classList;
891    }
892
893    private static class CoverageClassVisitor implements URLHandler.URLVisitor {
894        private List<String> classNames;
895
896        public CoverageClassVisitor(List<String> classNamesList) {
897            this.classNames = classNamesList;
898        }
899
900        /* (non-Javadoc)
901         * @see com.ibm.icu.impl.URLHandler.URLVisitor#visit(java.lang.String)
902         */
903        @Override
904        public void visit(String classPath) {
905            int ix = classPath.lastIndexOf(".class");
906            if (ix < 0) {
907                return;
908            }
909            String className = "com.ibm.icu" + classPath.substring(0, ix).replace('/', '.');
910
911            // Skip things in com.ibm.icu.dev; they're not relevant.
912            if (className.startsWith("com.ibm.icu.dev.")) {
913                return;
914            }
915            Class c;
916            try {
917                c = Class.forName(className);
918            } catch (ClassNotFoundException e) {
919                return;
920            }
921            int m = c.getModifiers();
922
923            if (className.equals("com.ibm.icu.text.PluralRules$FixedDecimal")) {
924                // Known Issue: "10268", "Serializable interface is not implemented in PluralRules$FixedDecimal"
925                return;
926            }
927
928            if (c.isEnum() || !serializable.isAssignableFrom(c)) {
929                //System.out.println("@@@ Skipping: " + className);
930                return;
931            }
932            if (!Modifier.isPublic(m) || Modifier.isInterface(m)) {
933                //System.out.println("@@@ Skipping: " + className);
934                return;
935            }
936
937            this.classNames.add(className);
938        }
939    }
940
941    public static void serializeObjects(File oof, Object[] objectsOut) throws IOException {
942        FileOutputStream fos = new FileOutputStream(oof);
943        ObjectOutputStream oos = new ObjectOutputStream(fos);
944        oos.writeObject(objectsOut);
945
946        oos.close();
947    }
948}
949