1/*
2 *******************************************************************************
3 * Copyright (C) 2013-2015, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 */
7package com.ibm.icu.dev.test.format;
8
9import java.io.ByteArrayInputStream;
10import java.io.ByteArrayOutputStream;
11import java.io.IOException;
12import java.io.ObjectInputStream;
13import java.io.ObjectOutputStream;
14import java.io.Serializable;
15import java.text.FieldPosition;
16import java.util.ArrayList;
17import java.util.Collections;
18import java.util.Comparator;
19import java.util.HashMap;
20import java.util.HashSet;
21import java.util.List;
22import java.util.Locale;
23import java.util.Map;
24import java.util.TreeMap;
25
26import com.ibm.icu.dev.test.TestFmwk;
27import com.ibm.icu.dev.test.serializable.SerializableTest;
28import com.ibm.icu.impl.Pair;
29import com.ibm.icu.impl.Utility;
30import com.ibm.icu.math.BigDecimal;
31import com.ibm.icu.text.MeasureFormat;
32import com.ibm.icu.text.MeasureFormat.FormatWidth;
33import com.ibm.icu.text.NumberFormat;
34import com.ibm.icu.util.Currency;
35import com.ibm.icu.util.Measure;
36import com.ibm.icu.util.MeasureUnit;
37import com.ibm.icu.util.TimeUnit;
38import com.ibm.icu.util.TimeUnitAmount;
39import com.ibm.icu.util.ULocale;
40
41/**
42 * See https://sites.google.com/site/icusite/processes/release/tasks/standards?pli=1
43 * for information on how to update with each new release.
44 * @author markdavis
45 */
46public class MeasureUnitTest extends TestFmwk {
47
48    static class OrderedPair<F extends Comparable, S extends Comparable> extends Pair<F, S> implements Comparable<OrderedPair<F, S>> {
49
50        OrderedPair(F first, S second) {
51            super(first, second);
52        }
53
54        public static <F extends Comparable, S extends Comparable> OrderedPair<F, S> of(F first, S second) {
55            if (first == null || second == null) {
56                throw new IllegalArgumentException("OrderedPair.of requires non null values.");
57            }
58            return new OrderedPair<F, S>(first, second);
59        }
60
61        public int compareTo(OrderedPair<F, S> other) {
62            int result = first.compareTo(other.first);
63            if (result != 0) {
64                return result;
65            }
66            return second.compareTo(other.second);
67        }
68    }
69
70    private static final String[] DRAFT_VERSIONS = {"54", "55", "56"};
71
72    private static final HashSet<String> DRAFT_VERSION_SET = new HashSet<String>();
73
74    private static final HashSet<String> TIME_CODES = new HashSet<String>();
75
76    private static final String[][] JAVA_VERSIONS = {
77        {"G_FORCE", "53"},
78        {"DEGREE", "53"},
79        {"ARC_MINUTE", "53"},
80        {"ARC_SECOND", "53"},
81        {"ACRE", "53"},
82        {"HECTARE", "53"},
83        {"SQUARE_FOOT", "53"},
84        {"SQUARE_KILOMETER", "53"},
85        {"SQUARE_METER", "53"},
86        {"SQUARE_MILE", "53"},
87        {"MILLISECOND", "53"},
88        {"CENTIMETER", "53"},
89        {"FOOT", "53"},
90        {"INCH", "53"},
91        {"KILOMETER", "53"},
92        {"LIGHT_YEAR", "53"},
93        {"METER", "53"},
94        {"MILE", "53"},
95        {"MILLIMETER", "53"},
96        {"PICOMETER", "53"},
97        {"YARD", "53"},
98        {"GRAM", "53"},
99        {"KILOGRAM", "53"},
100        {"OUNCE", "53"},
101        {"POUND", "53"},
102        {"HORSEPOWER", "53"},
103        {"KILOWATT", "53"},
104        {"WATT", "53"},
105        {"HECTOPASCAL", "53"},
106        {"INCH_HG", "53"},
107        {"MILLIBAR", "53"},
108        {"KILOMETER_PER_HOUR", "53"},
109        {"METER_PER_SECOND", "53"},
110        {"MILE_PER_HOUR", "53"},
111        {"CELSIUS", "53"},
112        {"FAHRENHEIT", "53"},
113        {"CUBIC_KILOMETER", "53"},
114        {"CUBIC_MILE", "53"},
115        {"LITER", "53"},
116        {"YEAR", "53"},
117        {"MONTH", "53"},
118        {"WEEK", "53"},
119        {"DAY", "53"},
120        {"HOUR", "53"},
121        {"MINUTE", "53"},
122        {"SECOND", "53"},
123        {"METER_PER_SECOND_SQUARED", "54"},
124        {"RADIAN", "54"},
125        {"SQUARE_CENTIMETER", "54"},
126        {"SQUARE_INCH", "54"},
127        {"SQUARE_YARD", "54"},
128        {"LITER_PER_KILOMETER", "54"},
129        {"MILE_PER_GALLON", "54"},
130        {"BIT", "54"},
131        {"BYTE", "54"},
132        {"GIGABIT", "54"},
133        {"GIGABYTE", "54"},
134        {"KILOBIT", "54"},
135        {"KILOBYTE", "54"},
136        {"MEGABIT", "54"},
137        {"MEGABYTE", "54"},
138        {"TERABIT", "54"},
139        {"TERABYTE", "54"},
140        {"MICROSECOND", "54"},
141        {"NANOSECOND", "54"},
142        {"AMPERE", "54"},
143        {"MILLIAMPERE", "54"},
144        {"OHM", "54"},
145        {"VOLT", "54"},
146        {"CALORIE", "54"},
147        {"FOODCALORIE", "54"},
148        {"JOULE", "54"},
149        {"KILOCALORIE", "54"},
150        {"KILOJOULE", "54"},
151        {"KILOWATT_HOUR", "54"},
152        {"GIGAHERTZ", "54"},
153        {"HERTZ", "54"},
154        {"KILOHERTZ", "54"},
155        {"MEGAHERTZ", "54"},
156        {"ASTRONOMICAL_UNIT", "54"},
157        {"DECIMETER", "54"},
158        {"FATHOM", "54"},
159        {"FURLONG", "54"},
160        {"MICROMETER", "54"},
161        {"NANOMETER", "54"},
162        {"NAUTICAL_MILE", "54"},
163        {"PARSEC", "54"},
164        {"LUX", "54"},
165        {"CARAT", "54"},
166        {"METRIC_TON", "54"},
167        {"MICROGRAM", "54"},
168        {"MILLIGRAM", "54"},
169        {"OUNCE_TROY", "54"},
170        {"STONE", "54"},
171        {"TON", "54"},
172        {"GIGAWATT", "54"},
173        {"MEGAWATT", "54"},
174        {"MILLIWATT", "54"},
175        {"MILLIMETER_OF_MERCURY", "54"},
176        {"POUND_PER_SQUARE_INCH", "54"},
177        {"KARAT", "54"},
178        {"KELVIN", "54"},
179        {"ACRE_FOOT", "54"},
180        {"BUSHEL", "54"},
181        {"CENTILITER", "54"},
182        {"CUBIC_CENTIMETER", "54"},
183        {"CUBIC_FOOT", "54"},
184        {"CUBIC_INCH", "54"},
185        {"CUBIC_METER", "54"},
186        {"CUBIC_YARD", "54"},
187        {"CUP", "54"},
188        {"DECILITER", "54"},
189        {"FLUID_OUNCE", "54"},
190        {"GALLON", "54"},
191        {"HECTOLITER", "54"},
192        {"MEGALITER", "54"},
193        {"MILLILITER", "54"},
194        {"PINT", "54"},
195        {"QUART", "54"},
196        {"TABLESPOON", "54"},
197        {"TEASPOON", "54"},
198        {"GENERIC_TEMPERATURE", "56"},
199        {"REVOLUTION_ANGLE", "56"},
200        {"LITER_PER_100KILOMETERS", "56"},
201        {"CENTURY", "56"},
202        {"MILE_SCANDINAVIAN", "56"},
203        {"KNOT", "56"},
204        {"CUP_METRIC", "56"},
205        {"PINT_METRIC", "56"},
206    };
207
208    private static final HashMap<String, String> JAVA_VERSION_MAP = new HashMap<String, String>();
209
210    static {
211        TIME_CODES.add("year");
212        TIME_CODES.add("month");
213        TIME_CODES.add("week");
214        TIME_CODES.add("day");
215        TIME_CODES.add("hour");
216        TIME_CODES.add("minute");
217        TIME_CODES.add("second");
218        for (String verNum : DRAFT_VERSIONS) {
219            DRAFT_VERSION_SET.add(verNum);
220        }
221        for (String[] funcNameAndVersion : JAVA_VERSIONS) {
222            JAVA_VERSION_MAP.put(funcNameAndVersion[0], funcNameAndVersion[1]);
223        }
224    }
225
226    /**
227     * @author markdavis
228     *
229     */
230    public static void main(String[] args) {
231        //generateConstants(); if (true) return;
232        new MeasureUnitTest().run(args);
233    }
234
235    //public void testZZZ() {
236    //    // various generateXXX calls go here, see
237    //    // http://site.icu-project.org/design/formatting/measureformat/updating-measure-unit
238    //}
239
240    public void TestCompatible53_1() {
241        MeasureUnit[] units = {
242                MeasureUnit.G_FORCE,
243                MeasureUnit.DEGREE,
244                MeasureUnit.ARC_MINUTE,
245                MeasureUnit.ARC_SECOND,
246                MeasureUnit.ACRE,
247                MeasureUnit.HECTARE,
248                MeasureUnit.SQUARE_FOOT,
249                MeasureUnit.SQUARE_KILOMETER,
250                MeasureUnit.SQUARE_METER,
251                MeasureUnit.SQUARE_MILE,
252                MeasureUnit.MILLISECOND,
253                MeasureUnit.CENTIMETER,
254                MeasureUnit.FOOT,
255                MeasureUnit.INCH,
256                MeasureUnit.KILOMETER,
257                MeasureUnit.LIGHT_YEAR,
258                MeasureUnit.METER,
259                MeasureUnit.MILE,
260                MeasureUnit.MILLIMETER,
261                MeasureUnit.PICOMETER,
262                MeasureUnit.YARD,
263                MeasureUnit.GRAM,
264                MeasureUnit.KILOGRAM,
265                MeasureUnit.OUNCE,
266                MeasureUnit.POUND,
267                MeasureUnit.HORSEPOWER,
268                MeasureUnit.KILOWATT,
269                MeasureUnit.WATT,
270                MeasureUnit.HECTOPASCAL,
271                MeasureUnit.INCH_HG,
272                MeasureUnit.MILLIBAR,
273                MeasureUnit.KILOMETER_PER_HOUR,
274                MeasureUnit.METER_PER_SECOND,
275                MeasureUnit.MILE_PER_HOUR,
276                MeasureUnit.CELSIUS,
277                MeasureUnit.FAHRENHEIT,
278                MeasureUnit.CUBIC_KILOMETER,
279                MeasureUnit.CUBIC_MILE,
280                MeasureUnit.LITER,
281                MeasureUnit.YEAR,
282                MeasureUnit.MONTH,
283                MeasureUnit.WEEK,
284                MeasureUnit.DAY,
285                MeasureUnit.HOUR,
286                MeasureUnit.MINUTE,
287                MeasureUnit.SECOND,
288        };
289        assertEquals("", 46, units.length);
290    }
291
292    public void TestCompatible54_1() {
293        MeasureUnit[] units = {
294                MeasureUnit.G_FORCE,
295                MeasureUnit.METER_PER_SECOND_SQUARED,
296                MeasureUnit.ARC_MINUTE,
297                MeasureUnit.ARC_SECOND,
298                MeasureUnit.DEGREE,
299                MeasureUnit.RADIAN,
300                MeasureUnit.ACRE,
301                MeasureUnit.HECTARE,
302                MeasureUnit.SQUARE_CENTIMETER,
303                MeasureUnit.SQUARE_FOOT,
304                MeasureUnit.SQUARE_INCH,
305                MeasureUnit.SQUARE_KILOMETER,
306                MeasureUnit.SQUARE_METER,
307                MeasureUnit.SQUARE_MILE,
308                MeasureUnit.SQUARE_YARD,
309                MeasureUnit.LITER_PER_KILOMETER,
310                MeasureUnit.MILE_PER_GALLON,
311                MeasureUnit.BIT,
312                MeasureUnit.BYTE,
313                MeasureUnit.GIGABIT,
314                MeasureUnit.GIGABYTE,
315                MeasureUnit.KILOBIT,
316                MeasureUnit.KILOBYTE,
317                MeasureUnit.MEGABIT,
318                MeasureUnit.MEGABYTE,
319                MeasureUnit.TERABIT,
320                MeasureUnit.TERABYTE,
321                MeasureUnit.DAY,
322                MeasureUnit.HOUR,
323                MeasureUnit.MICROSECOND,
324                MeasureUnit.MILLISECOND,
325                MeasureUnit.MINUTE,
326                MeasureUnit.MONTH,
327                MeasureUnit.NANOSECOND,
328                MeasureUnit.SECOND,
329                MeasureUnit.WEEK,
330                MeasureUnit.YEAR,
331                MeasureUnit.AMPERE,
332                MeasureUnit.MILLIAMPERE,
333                MeasureUnit.OHM,
334                MeasureUnit.VOLT,
335                MeasureUnit.CALORIE,
336                MeasureUnit.FOODCALORIE,
337                MeasureUnit.JOULE,
338                MeasureUnit.KILOCALORIE,
339                MeasureUnit.KILOJOULE,
340                MeasureUnit.KILOWATT_HOUR,
341                MeasureUnit.GIGAHERTZ,
342                MeasureUnit.HERTZ,
343                MeasureUnit.KILOHERTZ,
344                MeasureUnit.MEGAHERTZ,
345                MeasureUnit.ASTRONOMICAL_UNIT,
346                MeasureUnit.CENTIMETER,
347                MeasureUnit.DECIMETER,
348                MeasureUnit.FATHOM,
349                MeasureUnit.FOOT,
350                MeasureUnit.FURLONG,
351                MeasureUnit.INCH,
352                MeasureUnit.KILOMETER,
353                MeasureUnit.LIGHT_YEAR,
354                MeasureUnit.METER,
355                MeasureUnit.MICROMETER,
356                MeasureUnit.MILE,
357                MeasureUnit.MILLIMETER,
358                MeasureUnit.NANOMETER,
359                MeasureUnit.NAUTICAL_MILE,
360                MeasureUnit.PARSEC,
361                MeasureUnit.PICOMETER,
362                MeasureUnit.YARD,
363                MeasureUnit.LUX,
364                MeasureUnit.CARAT,
365                MeasureUnit.GRAM,
366                MeasureUnit.KILOGRAM,
367                MeasureUnit.METRIC_TON,
368                MeasureUnit.MICROGRAM,
369                MeasureUnit.MILLIGRAM,
370                MeasureUnit.OUNCE,
371                MeasureUnit.OUNCE_TROY,
372                MeasureUnit.POUND,
373                MeasureUnit.STONE,
374                MeasureUnit.TON,
375                MeasureUnit.GIGAWATT,
376                MeasureUnit.HORSEPOWER,
377                MeasureUnit.KILOWATT,
378                MeasureUnit.MEGAWATT,
379                MeasureUnit.MILLIWATT,
380                MeasureUnit.WATT,
381                MeasureUnit.HECTOPASCAL,
382                MeasureUnit.INCH_HG,
383                MeasureUnit.MILLIBAR,
384                MeasureUnit.MILLIMETER_OF_MERCURY,
385                MeasureUnit.POUND_PER_SQUARE_INCH,
386                MeasureUnit.KARAT,
387                MeasureUnit.KILOMETER_PER_HOUR,
388                MeasureUnit.METER_PER_SECOND,
389                MeasureUnit.MILE_PER_HOUR,
390                MeasureUnit.CELSIUS,
391                MeasureUnit.FAHRENHEIT,
392                MeasureUnit.KELVIN,
393                MeasureUnit.ACRE_FOOT,
394                MeasureUnit.BUSHEL,
395                MeasureUnit.CENTILITER,
396                MeasureUnit.CUBIC_CENTIMETER,
397                MeasureUnit.CUBIC_FOOT,
398                MeasureUnit.CUBIC_INCH,
399                MeasureUnit.CUBIC_KILOMETER,
400                MeasureUnit.CUBIC_METER,
401                MeasureUnit.CUBIC_MILE,
402                MeasureUnit.CUBIC_YARD,
403                MeasureUnit.CUP,
404                MeasureUnit.DECILITER,
405                MeasureUnit.FLUID_OUNCE,
406                MeasureUnit.GALLON,
407                MeasureUnit.HECTOLITER,
408                MeasureUnit.LITER,
409                MeasureUnit.MEGALITER,
410                MeasureUnit.MILLILITER,
411                MeasureUnit.PINT,
412                MeasureUnit.QUART,
413                MeasureUnit.TABLESPOON,
414                MeasureUnit.TEASPOON,
415        };
416        assertEquals("",  121, units.length);
417    }
418
419    public void TestCompatible55_1() {
420        MeasureUnit[] units = {
421                MeasureUnit.G_FORCE,
422                MeasureUnit.METER_PER_SECOND_SQUARED,
423                MeasureUnit.ARC_MINUTE,
424                MeasureUnit.ARC_SECOND,
425                MeasureUnit.DEGREE,
426                MeasureUnit.RADIAN,
427                MeasureUnit.ACRE,
428                MeasureUnit.HECTARE,
429                MeasureUnit.SQUARE_CENTIMETER,
430                MeasureUnit.SQUARE_FOOT,
431                MeasureUnit.SQUARE_INCH,
432                MeasureUnit.SQUARE_KILOMETER,
433                MeasureUnit.SQUARE_METER,
434                MeasureUnit.SQUARE_MILE,
435                MeasureUnit.SQUARE_YARD,
436                MeasureUnit.LITER_PER_KILOMETER,
437                MeasureUnit.MILE_PER_GALLON,
438                MeasureUnit.BIT,
439                MeasureUnit.BYTE,
440                MeasureUnit.GIGABIT,
441                MeasureUnit.GIGABYTE,
442                MeasureUnit.KILOBIT,
443                MeasureUnit.KILOBYTE,
444                MeasureUnit.MEGABIT,
445                MeasureUnit.MEGABYTE,
446                MeasureUnit.TERABIT,
447                MeasureUnit.TERABYTE,
448                MeasureUnit.DAY,
449                MeasureUnit.HOUR,
450                MeasureUnit.MICROSECOND,
451                MeasureUnit.MILLISECOND,
452                MeasureUnit.MINUTE,
453                MeasureUnit.MONTH,
454                MeasureUnit.NANOSECOND,
455                MeasureUnit.SECOND,
456                MeasureUnit.WEEK,
457                MeasureUnit.YEAR,
458                MeasureUnit.AMPERE,
459                MeasureUnit.MILLIAMPERE,
460                MeasureUnit.OHM,
461                MeasureUnit.VOLT,
462                MeasureUnit.CALORIE,
463                MeasureUnit.FOODCALORIE,
464                MeasureUnit.JOULE,
465                MeasureUnit.KILOCALORIE,
466                MeasureUnit.KILOJOULE,
467                MeasureUnit.KILOWATT_HOUR,
468                MeasureUnit.GIGAHERTZ,
469                MeasureUnit.HERTZ,
470                MeasureUnit.KILOHERTZ,
471                MeasureUnit.MEGAHERTZ,
472                MeasureUnit.ASTRONOMICAL_UNIT,
473                MeasureUnit.CENTIMETER,
474                MeasureUnit.DECIMETER,
475                MeasureUnit.FATHOM,
476                MeasureUnit.FOOT,
477                MeasureUnit.FURLONG,
478                MeasureUnit.INCH,
479                MeasureUnit.KILOMETER,
480                MeasureUnit.LIGHT_YEAR,
481                MeasureUnit.METER,
482                MeasureUnit.MICROMETER,
483                MeasureUnit.MILE,
484                MeasureUnit.MILLIMETER,
485                MeasureUnit.NANOMETER,
486                MeasureUnit.NAUTICAL_MILE,
487                MeasureUnit.PARSEC,
488                MeasureUnit.PICOMETER,
489                MeasureUnit.YARD,
490                MeasureUnit.LUX,
491                MeasureUnit.CARAT,
492                MeasureUnit.GRAM,
493                MeasureUnit.KILOGRAM,
494                MeasureUnit.METRIC_TON,
495                MeasureUnit.MICROGRAM,
496                MeasureUnit.MILLIGRAM,
497                MeasureUnit.OUNCE,
498                MeasureUnit.OUNCE_TROY,
499                MeasureUnit.POUND,
500                MeasureUnit.STONE,
501                MeasureUnit.TON,
502                MeasureUnit.GIGAWATT,
503                MeasureUnit.HORSEPOWER,
504                MeasureUnit.KILOWATT,
505                MeasureUnit.MEGAWATT,
506                MeasureUnit.MILLIWATT,
507                MeasureUnit.WATT,
508                MeasureUnit.HECTOPASCAL,
509                MeasureUnit.INCH_HG,
510                MeasureUnit.MILLIBAR,
511                MeasureUnit.MILLIMETER_OF_MERCURY,
512                MeasureUnit.POUND_PER_SQUARE_INCH,
513                MeasureUnit.KARAT,
514                MeasureUnit.KILOMETER_PER_HOUR,
515                MeasureUnit.METER_PER_SECOND,
516                MeasureUnit.MILE_PER_HOUR,
517                MeasureUnit.CELSIUS,
518                MeasureUnit.FAHRENHEIT,
519                MeasureUnit.GENERIC_TEMPERATURE,
520                MeasureUnit.KELVIN,
521                MeasureUnit.ACRE_FOOT,
522                MeasureUnit.BUSHEL,
523                MeasureUnit.CENTILITER,
524                MeasureUnit.CUBIC_CENTIMETER,
525                MeasureUnit.CUBIC_FOOT,
526                MeasureUnit.CUBIC_INCH,
527                MeasureUnit.CUBIC_KILOMETER,
528                MeasureUnit.CUBIC_METER,
529                MeasureUnit.CUBIC_MILE,
530                MeasureUnit.CUBIC_YARD,
531                MeasureUnit.CUP,
532                MeasureUnit.DECILITER,
533                MeasureUnit.FLUID_OUNCE,
534                MeasureUnit.GALLON,
535                MeasureUnit.HECTOLITER,
536                MeasureUnit.LITER,
537                MeasureUnit.MEGALITER,
538                MeasureUnit.MILLILITER,
539                MeasureUnit.PINT,
540                MeasureUnit.QUART,
541                MeasureUnit.TABLESPOON,
542                MeasureUnit.TEASPOON,
543        };
544        assertEquals("",  122, units.length);
545    }
546
547    public void TestCompatible56_1() {
548        MeasureUnit[] units = {
549                MeasureUnit.G_FORCE,
550                MeasureUnit.METER_PER_SECOND_SQUARED,
551                MeasureUnit.ARC_MINUTE,
552                MeasureUnit.ARC_SECOND,
553                MeasureUnit.DEGREE,
554                MeasureUnit.RADIAN,
555                MeasureUnit.REVOLUTION_ANGLE,
556                MeasureUnit.ACRE,
557                MeasureUnit.HECTARE,
558                MeasureUnit.SQUARE_CENTIMETER,
559                MeasureUnit.SQUARE_FOOT,
560                MeasureUnit.SQUARE_INCH,
561                MeasureUnit.SQUARE_KILOMETER,
562                MeasureUnit.SQUARE_METER,
563                MeasureUnit.SQUARE_MILE,
564                MeasureUnit.SQUARE_YARD,
565                MeasureUnit.LITER_PER_100KILOMETERS,
566                MeasureUnit.LITER_PER_KILOMETER,
567                MeasureUnit.MILE_PER_GALLON,
568                MeasureUnit.BIT,
569                MeasureUnit.BYTE,
570                MeasureUnit.GIGABIT,
571                MeasureUnit.GIGABYTE,
572                MeasureUnit.KILOBIT,
573                MeasureUnit.KILOBYTE,
574                MeasureUnit.MEGABIT,
575                MeasureUnit.MEGABYTE,
576                MeasureUnit.TERABIT,
577                MeasureUnit.TERABYTE,
578                MeasureUnit.CENTURY,
579                MeasureUnit.DAY,
580                MeasureUnit.HOUR,
581                MeasureUnit.MICROSECOND,
582                MeasureUnit.MILLISECOND,
583                MeasureUnit.MINUTE,
584                MeasureUnit.MONTH,
585                MeasureUnit.NANOSECOND,
586                MeasureUnit.SECOND,
587                MeasureUnit.WEEK,
588                MeasureUnit.YEAR,
589                MeasureUnit.AMPERE,
590                MeasureUnit.MILLIAMPERE,
591                MeasureUnit.OHM,
592                MeasureUnit.VOLT,
593                MeasureUnit.CALORIE,
594                MeasureUnit.FOODCALORIE,
595                MeasureUnit.JOULE,
596                MeasureUnit.KILOCALORIE,
597                MeasureUnit.KILOJOULE,
598                MeasureUnit.KILOWATT_HOUR,
599                MeasureUnit.GIGAHERTZ,
600                MeasureUnit.HERTZ,
601                MeasureUnit.KILOHERTZ,
602                MeasureUnit.MEGAHERTZ,
603                MeasureUnit.ASTRONOMICAL_UNIT,
604                MeasureUnit.CENTIMETER,
605                MeasureUnit.DECIMETER,
606                MeasureUnit.FATHOM,
607                MeasureUnit.FOOT,
608                MeasureUnit.FURLONG,
609                MeasureUnit.INCH,
610                MeasureUnit.KILOMETER,
611                MeasureUnit.LIGHT_YEAR,
612                MeasureUnit.METER,
613                MeasureUnit.MICROMETER,
614                MeasureUnit.MILE,
615                MeasureUnit.MILE_SCANDINAVIAN,
616                MeasureUnit.MILLIMETER,
617                MeasureUnit.NANOMETER,
618                MeasureUnit.NAUTICAL_MILE,
619                MeasureUnit.PARSEC,
620                MeasureUnit.PICOMETER,
621                MeasureUnit.YARD,
622                MeasureUnit.LUX,
623                MeasureUnit.CARAT,
624                MeasureUnit.GRAM,
625                MeasureUnit.KILOGRAM,
626                MeasureUnit.METRIC_TON,
627                MeasureUnit.MICROGRAM,
628                MeasureUnit.MILLIGRAM,
629                MeasureUnit.OUNCE,
630                MeasureUnit.OUNCE_TROY,
631                MeasureUnit.POUND,
632                MeasureUnit.STONE,
633                MeasureUnit.TON,
634                MeasureUnit.GIGAWATT,
635                MeasureUnit.HORSEPOWER,
636                MeasureUnit.KILOWATT,
637                MeasureUnit.MEGAWATT,
638                MeasureUnit.MILLIWATT,
639                MeasureUnit.WATT,
640                MeasureUnit.HECTOPASCAL,
641                MeasureUnit.INCH_HG,
642                MeasureUnit.MILLIBAR,
643                MeasureUnit.MILLIMETER_OF_MERCURY,
644                MeasureUnit.POUND_PER_SQUARE_INCH,
645                MeasureUnit.KARAT,
646                MeasureUnit.KILOMETER_PER_HOUR,
647                MeasureUnit.KNOT,
648                MeasureUnit.METER_PER_SECOND,
649                MeasureUnit.MILE_PER_HOUR,
650                MeasureUnit.CELSIUS,
651                MeasureUnit.FAHRENHEIT,
652                MeasureUnit.GENERIC_TEMPERATURE,
653                MeasureUnit.KELVIN,
654                MeasureUnit.ACRE_FOOT,
655                MeasureUnit.BUSHEL,
656                MeasureUnit.CENTILITER,
657                MeasureUnit.CUBIC_CENTIMETER,
658                MeasureUnit.CUBIC_FOOT,
659                MeasureUnit.CUBIC_INCH,
660                MeasureUnit.CUBIC_KILOMETER,
661                MeasureUnit.CUBIC_METER,
662                MeasureUnit.CUBIC_MILE,
663                MeasureUnit.CUBIC_YARD,
664                MeasureUnit.CUP,
665                MeasureUnit.CUP_METRIC,
666                MeasureUnit.DECILITER,
667                MeasureUnit.FLUID_OUNCE,
668                MeasureUnit.GALLON,
669                MeasureUnit.HECTOLITER,
670                MeasureUnit.LITER,
671                MeasureUnit.MEGALITER,
672                MeasureUnit.MILLILITER,
673                MeasureUnit.PINT,
674                MeasureUnit.PINT_METRIC,
675                MeasureUnit.QUART,
676                MeasureUnit.TABLESPOON,
677                MeasureUnit.TEASPOON,
678        };
679        assertEquals("",  129, units.length);
680    }
681
682    public void TestExamplesInDocs() {
683        MeasureFormat fmtFr = MeasureFormat.getInstance(
684                ULocale.FRENCH, FormatWidth.SHORT);
685        Measure measure = new Measure(23, MeasureUnit.CELSIUS);
686        assertEquals("23 °C", "23 °C", fmtFr.format(measure));
687        Measure measureF = new Measure(70, MeasureUnit.FAHRENHEIT);
688        assertEquals("70 °F", "70 °F", fmtFr.format(measureF));
689        MeasureFormat fmtFrFull = MeasureFormat.getInstance(
690                ULocale.FRENCH, FormatWidth.WIDE);
691        assertEquals(
692                "70 pied et 5,3 pouces",
693                "70 pieds et 5,3 pouces",
694                fmtFrFull.formatMeasures(
695                        new Measure(70, MeasureUnit.FOOT),
696                        new Measure(5.3, MeasureUnit.INCH)));
697        assertEquals(
698                "1 pied et 1 pouce",
699                "1 pied et 1 pouce",
700                fmtFrFull.formatMeasures(
701                        new Measure(1, MeasureUnit.FOOT),
702                        new Measure(1, MeasureUnit.INCH)));
703        MeasureFormat fmtFrNarrow = MeasureFormat.getInstance(
704                ULocale.FRENCH, FormatWidth.NARROW);
705        assertEquals(
706                "1′ 1″",
707                "1′ 1″",
708                fmtFrNarrow.formatMeasures(
709                        new Measure(1, MeasureUnit.FOOT),
710                        new Measure(1, MeasureUnit.INCH)));
711        MeasureFormat fmtEn = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
712        assertEquals(
713                "1 inch, 2 feet",
714                "1 inch, 2 feet",
715                fmtEn.formatMeasures(
716                        new Measure(1, MeasureUnit.INCH),
717                        new Measure(2, MeasureUnit.FOOT)));
718    }
719
720    public void TestFormatPeriodEn() {
721        TimeUnitAmount[] _19m = {new TimeUnitAmount(19.0, TimeUnit.MINUTE)};
722        TimeUnitAmount[] _1h_23_5s = {
723                new TimeUnitAmount(1.0, TimeUnit.HOUR),
724                new TimeUnitAmount(23.5, TimeUnit.SECOND)};
725        TimeUnitAmount[] _1h_23_5m = {
726                new TimeUnitAmount(1.0, TimeUnit.HOUR),
727                new TimeUnitAmount(23.5, TimeUnit.MINUTE)};
728        TimeUnitAmount[] _1h_0m_23s = {
729                new TimeUnitAmount(1.0, TimeUnit.HOUR),
730                new TimeUnitAmount(0.0, TimeUnit.MINUTE),
731                new TimeUnitAmount(23.0, TimeUnit.SECOND)};
732        TimeUnitAmount[] _2y_5M_3w_4d = {
733                new TimeUnitAmount(2.0, TimeUnit.YEAR),
734                new TimeUnitAmount(5.0, TimeUnit.MONTH),
735                new TimeUnitAmount(3.0, TimeUnit.WEEK),
736                new TimeUnitAmount(4.0, TimeUnit.DAY)};
737        TimeUnitAmount[] _1m_59_9996s = {
738                new TimeUnitAmount(1.0, TimeUnit.MINUTE),
739                new TimeUnitAmount(59.9996, TimeUnit.SECOND)};
740        TimeUnitAmount[] _5h_17m = {
741                new TimeUnitAmount(5.0, TimeUnit.HOUR),
742                new TimeUnitAmount(17.0, TimeUnit.MINUTE)};
743        TimeUnitAmount[] _neg5h_17m = {
744                new TimeUnitAmount(-5.0, TimeUnit.HOUR),
745                new TimeUnitAmount(17.0, TimeUnit.MINUTE)};
746        TimeUnitAmount[] _19m_28s = {
747                new TimeUnitAmount(19.0, TimeUnit.MINUTE),
748                new TimeUnitAmount(28.0, TimeUnit.SECOND)};
749        TimeUnitAmount[] _0h_0m_9s = {
750                new TimeUnitAmount(0.0, TimeUnit.HOUR),
751                new TimeUnitAmount(0.0, TimeUnit.MINUTE),
752                new TimeUnitAmount(9.0, TimeUnit.SECOND)};
753        TimeUnitAmount[] _0h_0m_17s = {
754                new TimeUnitAmount(0.0, TimeUnit.HOUR),
755                new TimeUnitAmount(0.0, TimeUnit.MINUTE),
756                new TimeUnitAmount(17.0, TimeUnit.SECOND)};
757        TimeUnitAmount[] _6h_56_92m = {
758                new TimeUnitAmount(6.0, TimeUnit.HOUR),
759                new TimeUnitAmount(56.92, TimeUnit.MINUTE)};
760        TimeUnitAmount[] _3h_4s_5m = {
761                new TimeUnitAmount(3.0, TimeUnit.HOUR),
762                new TimeUnitAmount(4.0, TimeUnit.SECOND),
763                new TimeUnitAmount(5.0, TimeUnit.MINUTE)};
764        TimeUnitAmount[] _6_7h_56_92m = {
765                new TimeUnitAmount(6.7, TimeUnit.HOUR),
766                new TimeUnitAmount(56.92, TimeUnit.MINUTE)};
767        TimeUnitAmount[] _3h_5h = {
768                new TimeUnitAmount(3.0, TimeUnit.HOUR),
769                new TimeUnitAmount(5.0, TimeUnit.HOUR)};
770
771        Object[][] fullData = {
772                {_1m_59_9996s, "1 minute, 59.9996 seconds"},
773                {_19m, "19 minutes"},
774                {_1h_23_5s, "1 hour, 23.5 seconds"},
775                {_1h_23_5m, "1 hour, 23.5 minutes"},
776                {_1h_0m_23s, "1 hour, 0 minutes, 23 seconds"},
777                {_2y_5M_3w_4d, "2 years, 5 months, 3 weeks, 4 days"}};
778        Object[][] abbrevData = {
779                {_1m_59_9996s, "1 min, 59.9996 sec"},
780                {_19m, "19 min"},
781                {_1h_23_5s, "1 hr, 23.5 sec"},
782                {_1h_23_5m, "1 hr, 23.5 min"},
783                {_1h_0m_23s, "1 hr, 0 min, 23 sec"},
784                {_2y_5M_3w_4d, "2 yrs, 5 mths, 3 wks, 4 days"}};
785        Object[][] narrowData = {
786                {_1m_59_9996s, "1m 59.9996s"},
787                {_19m, "19m"},
788                {_1h_23_5s, "1h 23.5s"},
789                {_1h_23_5m, "1h 23.5m"},
790                {_1h_0m_23s, "1h 0m 23s"},
791                {_2y_5M_3w_4d, "2y 5m 3w 4d"}};
792
793
794        Object[][] numericData = {
795                {_1m_59_9996s, "1:59.9996"},
796                {_19m, "19m"},
797                {_1h_23_5s, "1:00:23.5"},
798                {_1h_0m_23s, "1:00:23"},
799                {_1h_23_5m, "1:23.5"},
800                {_5h_17m, "5:17"},
801                {_neg5h_17m, "-5h 17m"},
802                {_19m_28s, "19:28"},
803                {_2y_5M_3w_4d, "2y 5m 3w 4d"},
804                {_0h_0m_9s, "0:00:09"},
805                {_6h_56_92m, "6:56.92"},
806                {_6_7h_56_92m, "6:56.92"},
807                {_3h_4s_5m, "3h 4s 5m"},
808                {_3h_5h, "3h 5h"}};
809        Object[][] fullDataDe = {
810                {_1m_59_9996s, "1 Minute und 59,9996 Sekunden"},
811                {_19m, "19 Minuten"},
812                {_1h_23_5s, "1 Stunde und 23,5 Sekunden"},
813                {_1h_23_5m, "1 Stunde und 23,5 Minuten"},
814                {_1h_0m_23s, "1 Stunde, 0 Minuten und 23 Sekunden"},
815                {_2y_5M_3w_4d, "2 Jahre, 5 Monate, 3 Wochen und 4 Tage"}};
816        Object[][] numericDataDe = {
817                {_1m_59_9996s, "1:59,9996"},
818                {_19m, "19 Min."},
819                {_1h_23_5s, "1:00:23,5"},
820                {_1h_0m_23s, "1:00:23"},
821                {_1h_23_5m, "1:23,5"},
822                {_5h_17m, "5:17"},
823                {_19m_28s, "19:28"},
824                {_2y_5M_3w_4d, "2 J, 5 M, 3 W und 4 T"},
825                {_0h_0m_17s, "0:00:17"},
826                {_6h_56_92m, "6:56,92"},
827                {_3h_5h, "3 Std., 5 Std."}};
828
829        NumberFormat nf = NumberFormat.getNumberInstance(ULocale.ENGLISH);
830        nf.setMaximumFractionDigits(4);
831        MeasureFormat mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE, nf);
832        verifyFormatPeriod("en FULL", mf, fullData);
833        mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT, nf);
834        verifyFormatPeriod("en SHORT", mf, abbrevData);
835        mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.NARROW, nf);
836        verifyFormatPeriod("en NARROW", mf, narrowData);
837        mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.NUMERIC, nf);
838        verifyFormatPeriod("en NUMERIC", mf, numericData);
839
840        nf = NumberFormat.getNumberInstance(ULocale.GERMAN);
841        nf.setMaximumFractionDigits(4);
842        mf = MeasureFormat.getInstance(ULocale.GERMAN, FormatWidth.WIDE, nf);
843        verifyFormatPeriod("de FULL", mf, fullDataDe);
844        mf = MeasureFormat.getInstance(ULocale.GERMAN, FormatWidth.NUMERIC, nf);
845        verifyFormatPeriod("de NUMERIC", mf, numericDataDe);
846
847        // Same tests, with Java Locale
848        nf = NumberFormat.getNumberInstance(Locale.GERMAN);
849        nf.setMaximumFractionDigits(4);
850        mf = MeasureFormat.getInstance(Locale.GERMAN, FormatWidth.WIDE, nf);
851        verifyFormatPeriod("de FULL(Java Locale)", mf, fullDataDe);
852        mf = MeasureFormat.getInstance(Locale.GERMAN, FormatWidth.NUMERIC, nf);
853        verifyFormatPeriod("de NUMERIC(Java Locale)", mf, numericDataDe);
854
855    }
856
857    private void verifyFormatPeriod(String desc, MeasureFormat mf, Object[][] testData) {
858        StringBuilder builder = new StringBuilder();
859        boolean failure = false;
860        for (Object[] testCase : testData) {
861            String actual = mf.format((Measure[]) testCase[0]);
862            if (!testCase[1].equals(actual)) {
863                builder.append(String.format("%s: Expected: '%s', got: '%s'\n", desc, testCase[1], actual));
864                failure = true;
865            }
866        }
867        if (failure) {
868            errln(builder.toString());
869        }
870    }
871
872    public void Test10219FractionalPlurals() {
873        double[] values = {1.588, 1.011};
874        String[][] expected = {
875                {"1 minute", "1.5 minutes", "1.58 minutes"},
876                {"1 minute", "1.0 minutes", "1.01 minutes"}
877        };
878        for (int j = 0; j < values.length; j++) {
879            for (int i = 0; i < expected[j].length; i++) {
880                NumberFormat nf = NumberFormat.getNumberInstance(ULocale.ENGLISH);
881                nf.setRoundingMode(BigDecimal.ROUND_DOWN);
882                nf.setMinimumFractionDigits(i);
883                nf.setMaximumFractionDigits(i);
884                MeasureFormat mf = MeasureFormat.getInstance(
885                        ULocale.ENGLISH, FormatWidth.WIDE, nf);
886                assertEquals("Test10219", expected[j][i], mf.format(new Measure(values[j], MeasureUnit.MINUTE)));
887            }
888        }
889    }
890
891    public void TestGreek() {
892        String[] locales = {"el_GR", "el"};
893        final MeasureUnit[] units = new MeasureUnit[]{
894                MeasureUnit.SECOND,
895                MeasureUnit.MINUTE,
896                MeasureUnit.HOUR,
897                MeasureUnit.DAY,
898                MeasureUnit.WEEK,
899                MeasureUnit.MONTH,
900                MeasureUnit.YEAR};
901        FormatWidth[] styles = new FormatWidth[] {FormatWidth.WIDE, FormatWidth.SHORT};
902        int[] numbers = new int[] {1, 7};
903        String[] expected = {
904                // "el_GR" 1 wide
905                "1 δευτερόλεπτο",
906                "1 λεπτό",
907                "1 ώρα",
908                "1 ημέρα",
909                "1 εβδομάδα",
910                "1 μήνας",
911                "1 έτος",
912                // "el_GR" 1 short
913                "1 δευτ.",
914                "1 λεπ.",
915                "1 ώρα",
916                "1 ημέρα",
917                "1 εβδ.",
918                "1 μήν.",
919                "1 έτ.",	        // year (one)
920                // "el_GR" 7 wide
921                "7 δευτερόλεπτα",
922                "7 λεπτά",
923                "7 ώρες",
924                "7 ημέρες",
925                "7 εβδομάδες",
926                "7 μήνες",
927                "7 έτη",
928                // "el_GR" 7 short
929                "7 δευτ.",
930                "7 λεπ.",
931                "7 ώρ.",		    // hour (other)
932                "7 ημέρες",
933                "7 εβδ.",
934                "7 μήν.",
935                "7 έτ.",            // year (other)
936                // "el" 1 wide
937                "1 δευτερόλεπτο",
938                "1 λεπτό",
939                "1 ώρα",
940                "1 ημέρα",
941                "1 εβδομάδα",
942                "1 μήνας",
943                "1 έτος",
944                // "el" 1 short
945                "1 δευτ.",
946                "1 λεπ.",
947                "1 ώρα",
948                "1 ημέρα",
949                "1 εβδ.",
950                "1 μήν.",
951                "1 έτ.",	        // year (one)
952                // "el" 7 wide
953                "7 δευτερόλεπτα",
954                "7 λεπτά",
955                "7 ώρες",
956                "7 ημέρες",
957                "7 εβδομάδες",
958                "7 μήνες",
959                "7 έτη",
960                // "el" 7 short
961                "7 δευτ.",
962                "7 λεπ.",
963                "7 ώρ.",		    // hour (other)
964                "7 ημέρες",
965                "7 εβδ.",
966                "7 μήν.",
967                "7 έτ."};           // year (other
968        int counter = 0;
969        String formatted;
970        for ( int locIndex = 0; locIndex < locales.length; ++locIndex ) {
971            for( int numIndex = 0; numIndex < numbers.length; ++numIndex ) {
972                for ( int styleIndex = 0; styleIndex < styles.length; ++styleIndex ) {
973                    for ( int unitIndex = 0; unitIndex < units.length; ++unitIndex ) {
974                        Measure m = new Measure(numbers[numIndex], units[unitIndex]);
975                        MeasureFormat fmt = MeasureFormat.getInstance(new ULocale(locales[locIndex]), styles[styleIndex]);
976                        formatted = fmt.format(m);
977                        assertEquals(
978                                "locale: " + locales[locIndex]
979                                        + ", style: " + styles[styleIndex]
980                                                + ", units: " + units[unitIndex]
981                                                        + ", value: " + numbers[numIndex],
982                                                expected[counter], formatted);
983                        ++counter;
984                    }
985                }
986            }
987        }
988    }
989
990    public void testAUnit() {
991        String lastType = null;
992        for (MeasureUnit expected : MeasureUnit.getAvailable()) {
993            String type = expected.getType();
994            String code = expected.getSubtype();
995            if (!type.equals(lastType)) {
996                logln(type);
997                lastType = type;
998            }
999            MeasureUnit actual = MeasureUnit.internalGetInstance(type, code);
1000            assertSame("Identity check", expected, actual);
1001        }
1002    }
1003
1004    public void testFormatSingleArg() {
1005        MeasureFormat mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
1006        assertEquals("", "5 meters", mf.format(new Measure(5, MeasureUnit.METER)));
1007    }
1008
1009    public void testFormatMeasuresZeroArg() {
1010        MeasureFormat mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
1011        assertEquals("", "", mf.formatMeasures());
1012    }
1013
1014    public void testFormatMeasuresOneArg() {
1015        MeasureFormat mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
1016        assertEquals("", "5 meters", mf.formatMeasures(new Measure(5, MeasureUnit.METER)));
1017    }
1018
1019
1020
1021    public void testMultiples() {
1022        ULocale russia = new ULocale("ru");
1023        Object[][] data = new Object[][] {
1024                {ULocale.ENGLISH, FormatWidth.WIDE, "2 miles, 1 foot, 2.3 inches"},
1025                {ULocale.ENGLISH, FormatWidth.SHORT, "2 mi, 1 ft, 2.3 in"},
1026                {ULocale.ENGLISH, FormatWidth.NARROW, "2mi 1\u2032 2.3\u2033"},
1027                {russia, FormatWidth.WIDE,   "2 \u043C\u0438\u043B\u0438 1 \u0444\u0443\u0442 \u0438 2,3 \u0434\u044E\u0439\u043C\u0430"},
1028                {russia, FormatWidth.SHORT,  "2 \u043C\u0438\u043B\u0438 1 \u0444\u0443\u0442 \u0438 2,3 \u0434\u044E\u0439\u043C."},
1029                {russia, FormatWidth.NARROW, "2 \u043C\u0438\u043B\u044C 1 \u0444\u0443\u0442 2,3 \u0434\u044E\u0439\u043C\u0430"},
1030        };
1031        for (Object[] row : data) {
1032            MeasureFormat mf = MeasureFormat.getInstance(
1033                    (ULocale) row[0], (FormatWidth) row[1]);
1034            assertEquals(
1035                    "testMultiples",
1036                    row[2],
1037                    mf.formatMeasures(
1038                            new Measure(2, MeasureUnit.MILE),
1039                            new Measure(1, MeasureUnit.FOOT),
1040                            new Measure(2.3, MeasureUnit.INCH)));
1041        }
1042    }
1043
1044    public void testSimplePer() {
1045        Object DONT_CARE = null;
1046        Object[][] data = new Object[][] {
1047                // per unit pattern
1048                {FormatWidth.WIDE, 1.0, MeasureUnit.SECOND, "1 pound per second", DONT_CARE, 0, 0},
1049                {FormatWidth.WIDE, 2.0, MeasureUnit.SECOND, "2 pounds per second", DONT_CARE, 0, 0},
1050                // compound pattern
1051                {FormatWidth.WIDE, 1.0, MeasureUnit.MINUTE, "1 pound per minute", DONT_CARE, 0, 0},
1052                {FormatWidth.WIDE, 2.0, MeasureUnit.MINUTE, "2 pounds per minute", DONT_CARE, 0, 0},
1053                // per unit
1054                {FormatWidth.SHORT, 1.0, MeasureUnit.SECOND, "1 lb/s", DONT_CARE, 0, 0},
1055                {FormatWidth.SHORT, 2.0, MeasureUnit.SECOND, "2 lb/s", DONT_CARE, 0, 0},
1056                // compound
1057                {FormatWidth.SHORT, 1.0, MeasureUnit.MINUTE, "1 lb/min", DONT_CARE, 0, 0},
1058                {FormatWidth.SHORT, 2.0, MeasureUnit.MINUTE, "2 lb/min", DONT_CARE, 0, 0},
1059                // per unit
1060                {FormatWidth.NARROW, 1.0, MeasureUnit.SECOND, "1#/s", DONT_CARE, 0, 0},
1061                {FormatWidth.NARROW, 2.0, MeasureUnit.SECOND, "2#/s", DONT_CARE, 0, 0},
1062                // compound
1063                {FormatWidth.NARROW, 1.0, MeasureUnit.MINUTE, "1#/min", DONT_CARE, 0, 0},
1064                {FormatWidth.NARROW, 2.0, MeasureUnit.MINUTE, "2#/min", DONT_CARE, 0, 0},
1065                // field positions
1066                {FormatWidth.SHORT, 23.3, MeasureUnit.SECOND, "23.3 lb/s", NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3},
1067                {FormatWidth.SHORT, 23.3, MeasureUnit.SECOND, "23.3 lb/s", NumberFormat.Field.INTEGER, 0, 2},
1068                {FormatWidth.SHORT, 23.3, MeasureUnit.MINUTE, "23.3 lb/min", NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3},
1069                {FormatWidth.SHORT, 23.3, MeasureUnit.MINUTE, "23.3 lb/min", NumberFormat.Field.INTEGER, 0, 2},
1070
1071        };
1072
1073        for (Object[] row : data) {
1074            FormatWidth formatWidth = (FormatWidth) row[0];
1075            Number amount = (Number) row[1];
1076            MeasureUnit perUnit = (MeasureUnit) row[2];
1077            String expected = row[3].toString();
1078            NumberFormat.Field field = (NumberFormat.Field) row[4];
1079            int startOffset = ((Integer) row[5]).intValue();
1080            int endOffset = ((Integer) row[6]).intValue();
1081            MeasureFormat mf = MeasureFormat.getInstance(
1082                    ULocale.ENGLISH, formatWidth);
1083            FieldPosition pos = field != null ? new FieldPosition(field) : new FieldPosition(0);
1084            String prefix = "Prefix: ";
1085            assertEquals(
1086                    "",
1087                    prefix + expected,
1088                    mf.formatMeasurePerUnit(
1089                            new Measure(amount, MeasureUnit.POUND),
1090                            perUnit,
1091                            new StringBuilder(prefix),
1092                            pos).toString());
1093            if (field != DONT_CARE) {
1094                assertEquals("startOffset", startOffset, pos.getBeginIndex() - prefix.length());
1095                assertEquals("endOffset", endOffset, pos.getEndIndex() - prefix.length());
1096            }
1097        }
1098    }
1099
1100    public void testNumeratorPlurals() {
1101        ULocale polish = new ULocale("pl");
1102        Object[][] data = new Object[][] {
1103                {1, "1 stopa na sekundę"},
1104                {2, "2 stopy na sekundę"},
1105                {5, "5 stóp na sekundę"},
1106                {1.5, "1,5 stopy na sekundę"}};
1107
1108        for (Object[] row : data) {
1109            MeasureFormat mf = MeasureFormat.getInstance(polish, FormatWidth.WIDE);
1110            assertEquals(
1111                    "",
1112                    row[1],
1113                    mf.formatMeasurePerUnit(
1114                            new Measure((Number) row[0], MeasureUnit.FOOT),
1115                            MeasureUnit.SECOND,
1116                            new StringBuilder(),
1117                            new FieldPosition(0)).toString());
1118        }
1119    }
1120
1121    public void testGram() {
1122        MeasureFormat mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT);
1123        assertEquals(
1124                "testGram",
1125                "1 g",
1126                mf.format(new Measure(1, MeasureUnit.GRAM)));
1127        assertEquals(
1128                "testGram",
1129                "1 G",
1130                mf.format(new Measure(1, MeasureUnit.G_FORCE)));
1131    }
1132
1133    public void testCurrencies() {
1134        Measure USD_1 = new Measure(1.0, Currency.getInstance("USD"));
1135        Measure USD_2 = new Measure(2.0, Currency.getInstance("USD"));
1136        Measure USD_NEG_1 = new Measure(-1.0, Currency.getInstance("USD"));
1137        MeasureFormat mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
1138        assertEquals("Wide currency", "-1.00 US dollars", mf.format(USD_NEG_1));
1139        assertEquals("Wide currency", "1.00 US dollars", mf.format(USD_1));
1140        assertEquals("Wide currency", "2.00 US dollars", mf.format(USD_2));
1141        mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT);
1142        assertEquals("short currency", "-USD1.00", mf.format(USD_NEG_1));
1143        assertEquals("short currency", "USD1.00", mf.format(USD_1));
1144        assertEquals("short currency", "USD2.00", mf.format(USD_2));
1145        mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.NARROW);
1146        assertEquals("narrow currency", "-$1.00", mf.format(USD_NEG_1));
1147        assertEquals("narrow currency", "$1.00", mf.format(USD_1));
1148        assertEquals("narrow currency", "$2.00", mf.format(USD_2));
1149        mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.NUMERIC);
1150        assertEquals("numeric currency", "-$1.00", mf.format(USD_NEG_1));
1151        assertEquals("numeric currency", "$1.00", mf.format(USD_1));
1152        assertEquals("numeric currency", "$2.00", mf.format(USD_2));
1153
1154        mf = MeasureFormat.getInstance(ULocale.JAPAN, FormatWidth.WIDE);
1155        assertEquals("Wide currency", "-1.00\u7C73\u30C9\u30EB", mf.format(USD_NEG_1));
1156        assertEquals("Wide currency", "1.00\u7C73\u30C9\u30EB", mf.format(USD_1));
1157        assertEquals("Wide currency", "2.00\u7C73\u30C9\u30EB", mf.format(USD_2));
1158    }
1159
1160    public void testFieldPosition() {
1161        MeasureFormat fmt = MeasureFormat.getInstance(
1162                ULocale.ENGLISH, FormatWidth.SHORT);
1163        FieldPosition pos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
1164        fmt.format(new Measure(43.5, MeasureUnit.FOOT), new StringBuffer("123456: "), pos);
1165        assertEquals("beginIndex", 10, pos.getBeginIndex());
1166        assertEquals("endIndex", 11, pos.getEndIndex());
1167
1168        pos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
1169        fmt.format(new Measure(43, MeasureUnit.FOOT), new StringBuffer(), pos);
1170        assertEquals("beginIndex", 0, pos.getBeginIndex());
1171        assertEquals("endIndex", 0, pos.getEndIndex());
1172    }
1173
1174    public void testFieldPositionMultiple() {
1175        MeasureFormat fmt = MeasureFormat.getInstance(
1176                ULocale.ENGLISH, FormatWidth.SHORT);
1177        FieldPosition pos = new FieldPosition(NumberFormat.Field.INTEGER);
1178        String result = fmt.formatMeasures(
1179                new StringBuilder(),
1180                pos,
1181                new Measure(354, MeasureUnit.METER),
1182                new Measure(23, MeasureUnit.CENTIMETER)).toString();
1183        assertEquals("result", "354 m, 23 cm", result);
1184
1185        // According to javadocs for {@link Format#format} FieldPosition is set to
1186        // beginning and end of first such field encountered instead of the last
1187        // such field encountered.
1188        assertEquals("beginIndex", 0, pos.getBeginIndex());
1189        assertEquals("endIndex", 3, pos.getEndIndex());
1190
1191        pos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
1192        result = fmt.formatMeasures(
1193                new StringBuilder("123456: "),
1194                pos,
1195                new Measure(354, MeasureUnit.METER),
1196                new Measure(23, MeasureUnit.CENTIMETER),
1197                new Measure(5.4, MeasureUnit.MILLIMETER)).toString();
1198        assertEquals("result", "123456: 354 m, 23 cm, 5.4 mm", result);
1199        assertEquals("beginIndex", 23, pos.getBeginIndex());
1200        assertEquals("endIndex", 24, pos.getEndIndex());
1201
1202        result = fmt.formatMeasures(
1203                new StringBuilder(),
1204                pos,
1205                new Measure(3, MeasureUnit.METER),
1206                new Measure(23, MeasureUnit.CENTIMETER),
1207                new Measure(5.4, MeasureUnit.MILLIMETER)).toString();
1208        assertEquals("result", "3 m, 23 cm, 5.4 mm", result);
1209        assertEquals("beginIndex", 13, pos.getBeginIndex());
1210        assertEquals("endIndex", 14, pos.getEndIndex());
1211
1212        pos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
1213        result = fmt.formatMeasures(
1214                new StringBuilder("123456: "),
1215                pos,
1216                new Measure(3, MeasureUnit.METER),
1217                new Measure(23, MeasureUnit.CENTIMETER),
1218                new Measure(5, MeasureUnit.MILLIMETER)).toString();
1219        assertEquals("result", "123456: 3 m, 23 cm, 5 mm", result);
1220        assertEquals("beginIndex", 0, pos.getBeginIndex());
1221        assertEquals("endIndex", 0, pos.getEndIndex());
1222
1223        pos = new FieldPosition(NumberFormat.Field.INTEGER);
1224        result = fmt.formatMeasures(
1225                new StringBuilder("123456: "),
1226                pos,
1227                new Measure(57, MeasureUnit.MILLIMETER)).toString();
1228        assertEquals("result", "123456: 57 mm", result);
1229        assertEquals("beginIndex", 8, pos.getBeginIndex());
1230        assertEquals("endIndex", 10, pos.getEndIndex());
1231
1232    }
1233
1234    public void testOldFormatWithList() {
1235        List<Measure> measures = new ArrayList<Measure>(2);
1236        measures.add(new Measure(5, MeasureUnit.ACRE));
1237        measures.add(new Measure(3000, MeasureUnit.SQUARE_FOOT));
1238        MeasureFormat fmt = MeasureFormat.getInstance(
1239                ULocale.ENGLISH, FormatWidth.WIDE);
1240        assertEquals("", "5 acres, 3,000 square feet", fmt.format(measures));
1241        assertEquals("", "5 acres", fmt.format(measures.subList(0, 1)));
1242        List<String> badList = new ArrayList<String>();
1243        badList.add("be");
1244        badList.add("you");
1245        try {
1246            fmt.format(badList);
1247            fail("Expected IllegalArgumentException.");
1248        } catch (IllegalArgumentException expected) {
1249           // Expected
1250        }
1251    }
1252
1253    public void testOldFormatWithArray() {
1254        Measure[] measures = new Measure[] {
1255                new Measure(5, MeasureUnit.ACRE),
1256                new Measure(3000, MeasureUnit.SQUARE_FOOT),
1257        };
1258        MeasureFormat fmt = MeasureFormat.getInstance(
1259                ULocale.ENGLISH, FormatWidth.WIDE);
1260        assertEquals("", "5 acres, 3,000 square feet", fmt.format(measures));
1261    }
1262
1263    public void testOldFormatBadArg() {
1264        MeasureFormat fmt = MeasureFormat.getInstance(
1265                ULocale.ENGLISH, FormatWidth.WIDE);
1266        try {
1267            fmt.format("be");
1268            fail("Expected IllegalArgumentExceptino.");
1269        } catch (IllegalArgumentException e) {
1270            // Expected
1271        }
1272    }
1273
1274    public void testUnitPerUnitResolution() {
1275        // Ticket 11274
1276        MeasureFormat fmt = MeasureFormat.getInstance(Locale.ENGLISH, FormatWidth.SHORT);
1277
1278        // This fails unless we resolve to MeasureUnit.POUND_PER_SQUARE_INCH
1279        assertEquals("", "50 psi",
1280                fmt.formatMeasurePerUnit(
1281                        new Measure(50, MeasureUnit.POUND),
1282                        MeasureUnit.SQUARE_INCH,
1283                        new StringBuilder(),
1284                        new FieldPosition(0)).toString());
1285    }
1286
1287    public void testEqHashCode() {
1288        MeasureFormat mf = MeasureFormat.getInstance(ULocale.CANADA, FormatWidth.SHORT);
1289        MeasureFormat mfeq = MeasureFormat.getInstance(ULocale.CANADA, FormatWidth.SHORT);
1290        MeasureFormat mfne = MeasureFormat.getInstance(ULocale.CANADA, FormatWidth.WIDE);
1291        MeasureFormat mfne2 = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT);
1292        verifyEqualsHashCode(mf, mfeq, mfne);
1293        verifyEqualsHashCode(mf, mfeq, mfne2);
1294    }
1295
1296    public void testEqHashCodeOfMeasure() {
1297        Measure _3feetDouble = new Measure(3.0, MeasureUnit.FOOT);
1298        Measure _3feetInt = new Measure(3, MeasureUnit.FOOT);
1299        Measure _4feetInt = new Measure(4, MeasureUnit.FOOT);
1300        verifyEqualsHashCode(_3feetDouble, _3feetInt, _4feetInt);
1301    }
1302
1303    public void testGetLocale() {
1304        MeasureFormat mf = MeasureFormat.getInstance(ULocale.GERMAN, FormatWidth.SHORT);
1305        assertEquals("", ULocale.GERMAN, mf.getLocale(ULocale.VALID_LOCALE));
1306    }
1307
1308    public void TestSerial() {
1309        checkStreamingEquality(MeasureUnit.CELSIUS);
1310        checkStreamingEquality(MeasureFormat.getInstance(ULocale.FRANCE, FormatWidth.NARROW));
1311        checkStreamingEquality(Currency.getInstance("EUR"));
1312        checkStreamingEquality(MeasureFormat.getInstance(ULocale.GERMAN, FormatWidth.SHORT));
1313        checkStreamingEquality(MeasureFormat.getCurrencyFormat(ULocale.ITALIAN));
1314    }
1315
1316    public void TestSerialFormatWidthEnum() {
1317        // FormatWidth enum values must map to the same ordinal values for all time in order for
1318        // serialization to work.
1319        assertEquals("FormatWidth.WIDE", 0, FormatWidth.WIDE.ordinal());
1320        assertEquals("FormatWidth.SHORT", 1, FormatWidth.SHORT.ordinal());
1321        assertEquals("FormatWidth.NARROW", 2, FormatWidth.NARROW.ordinal());
1322        assertEquals("FormatWidth.NUMERIC", 3, FormatWidth.NUMERIC.ordinal());
1323    }
1324
1325    public void testCurrencyFormatStandInForMeasureFormat() {
1326        MeasureFormat mf = MeasureFormat.getCurrencyFormat(ULocale.ENGLISH);
1327        assertEquals(
1328                "70 feet, 5.3 inches",
1329                "70 feet, 5.3 inches",
1330                mf.formatMeasures(
1331                        new Measure(70, MeasureUnit.FOOT),
1332                        new Measure(5.3, MeasureUnit.INCH)));
1333        assertEquals("getLocale", ULocale.ENGLISH, mf.getLocale());
1334        assertEquals("getNumberFormat", ULocale.ENGLISH, mf.getNumberFormat().getLocale(ULocale.VALID_LOCALE));
1335        assertEquals("getWidth", MeasureFormat.FormatWidth.WIDE, mf.getWidth());
1336    }
1337
1338    public void testCurrencyFormatLocale() {
1339        MeasureFormat mfu = MeasureFormat.getCurrencyFormat(ULocale.FRANCE);
1340        MeasureFormat mfj = MeasureFormat.getCurrencyFormat(Locale.FRANCE);
1341
1342        assertEquals("getCurrencyFormat ULocale/Locale", mfu, mfj);
1343    }
1344
1345    public void testDoubleZero() {
1346        ULocale en = new ULocale("en");
1347        NumberFormat nf = NumberFormat.getInstance(en);
1348        nf.setMinimumFractionDigits(2);
1349        nf.setMaximumFractionDigits(2);
1350        MeasureFormat mf = MeasureFormat.getInstance(en, FormatWidth.WIDE, nf);
1351        assertEquals(
1352                "Positive Rounding",
1353                "4 hours, 23 minutes, 16.00 seconds",
1354                mf.formatMeasures(
1355                        new Measure(4.7, MeasureUnit.HOUR),
1356                        new Measure(23, MeasureUnit.MINUTE),
1357                        new Measure(16, MeasureUnit.SECOND)));
1358        assertEquals(
1359                "Negative Rounding",
1360                "-4 hours, 23 minutes, 16.00 seconds",
1361                mf.formatMeasures(
1362                        new Measure(-4.7, MeasureUnit.HOUR),
1363                        new Measure(23, MeasureUnit.MINUTE),
1364                        new Measure(16, MeasureUnit.SECOND)));
1365
1366    }
1367
1368    public void testIndividualPluralFallback() {
1369        // See ticket #11986 "incomplete fallback in MeasureFormat".
1370        // In CLDR 28, fr_CA temperature-generic/short has only the "one" form,
1371        // and falls back to fr for the "other" form.
1372        MeasureFormat mf = MeasureFormat.getInstance(new ULocale("fr_CA"), FormatWidth.SHORT);
1373        Measure twoDeg = new Measure(2, MeasureUnit.GENERIC_TEMPERATURE);
1374        assertEquals("2 deg temp in fr_CA", "2°", mf.format(twoDeg));
1375    }
1376
1377    // DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
1378    // for MeasureFormat during the release process.
1379    static Map<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> getUnitsToPerParts() {
1380        TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
1381        Map<MeasureUnit, Pair<String, String>> unitsToPerStrings =
1382                new HashMap<MeasureUnit, Pair<String, String>>();
1383        Map<String, MeasureUnit> namesToUnits = new HashMap<String, MeasureUnit>();
1384        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1385            String type = entry.getKey();
1386            // Currency types are always atomic units, so we can skip these
1387            if (type.equals("currency")) {
1388                continue;
1389            }
1390            for (MeasureUnit unit : entry.getValue()) {
1391                String javaName = toJAVAName(unit);
1392                String[] nameParts = javaName.split("_PER_");
1393                if (nameParts.length == 1) {
1394                    namesToUnits.put(nameParts[0], unit);
1395                } else if (nameParts.length == 2) {
1396                    unitsToPerStrings.put(unit, Pair.of(nameParts[0], nameParts[1]));
1397                }
1398            }
1399        }
1400        Map<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> unitsToPerUnits =
1401                new HashMap<MeasureUnit, Pair<MeasureUnit, MeasureUnit>>();
1402        for (Map.Entry<MeasureUnit, Pair<String, String>> entry : unitsToPerStrings.entrySet()) {
1403            Pair<String, String> perStrings = entry.getValue();
1404            MeasureUnit unit = namesToUnits.get(perStrings.first);
1405            MeasureUnit perUnit = namesToUnits.get(perStrings.second);
1406            if (unit != null && perUnit != null) {
1407                unitsToPerUnits.put(entry.getKey(), Pair.of(unit, perUnit));
1408            }
1409        }
1410        return unitsToPerUnits;
1411    }
1412
1413    public void testParseObject() {
1414        MeasureFormat mf = MeasureFormat.getInstance(Locale.GERMAN, FormatWidth.NARROW);
1415        try {
1416            mf.parseObject("3m", null);
1417            fail("MeasureFormat.parseObject(String, ParsePosition) " +
1418                    "should throw an UnsupportedOperationException");
1419        } catch (UnsupportedOperationException expected) {
1420        }
1421    }
1422
1423    // DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
1424    // for MeasureFormat during the release process.
1425    static void generateCXXHConstants(String thisVersion) {
1426        Map<String, MeasureUnit> seen = new HashMap<String, MeasureUnit>();
1427        System.out.println();
1428        TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
1429        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1430            String type = entry.getKey();
1431            if (type.equals("currency")) {
1432                continue;
1433            }
1434            for (MeasureUnit unit : entry.getValue()) {
1435                String code = unit.getSubtype();
1436                String name = toCamelCase(unit);
1437                String javaName = toJAVAName(unit);
1438                checkForDup(seen, name, unit);
1439                if (isDraft(javaName)) {
1440                    System.out.println("#ifndef U_HIDE_DRAFT_API");
1441                }
1442                System.out.println("    /**");
1443                System.out.println("     * Returns unit of " + type + ": " + code + ".");
1444                System.out.println("     * Caller owns returned value and must free it.");
1445                System.out.println("     * @param status ICU error code.");
1446                if (isDraft(javaName)) {
1447                    System.out.println("     * @draft ICU " + getVersion(javaName, thisVersion));
1448                } else {
1449                    System.out.println("     * @stable ICU " + getVersion(javaName, thisVersion));
1450                }
1451                System.out.println("     */");
1452                System.out.printf("    static MeasureUnit *create%s(UErrorCode &status);\n\n", name);
1453                if (isDraft(javaName)) {
1454                    System.out.println("#endif /* U_HIDE_DRAFT_API */");
1455                }
1456            }
1457        }
1458    }
1459
1460    private static void checkForDup(
1461            Map<String, MeasureUnit> seen, String name, MeasureUnit unit) {
1462        if (seen.containsKey(name)) {
1463            throw new RuntimeException("\nCollision!!" + unit + ", " + seen.get(name));
1464        } else {
1465            seen.put(name, unit);
1466        }
1467    }
1468
1469    // DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
1470    // for MeasureFormat during the release process.
1471    static void updateJAVAVersions(String thisVersion) {
1472        System.out.println();
1473        Map<String, MeasureUnit> seen = new HashMap<String, MeasureUnit>();
1474        TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
1475        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1476            String type = entry.getKey();
1477            if (type.equals("currency")) {
1478                continue;
1479            }
1480            for (MeasureUnit unit : entry.getValue()) {
1481                String javaName = toJAVAName(unit);
1482                checkForDup(seen, javaName, unit);
1483                if (!JAVA_VERSION_MAP.containsKey(javaName)) {
1484                    System.out.printf("        {\"%s\", \"%s\"},\n", javaName, thisVersion);
1485                }
1486            }
1487        }
1488    }
1489
1490    static TreeMap<String, List<MeasureUnit>> getAllUnits() {
1491        TreeMap<String, List<MeasureUnit>> allUnits = new TreeMap<String, List<MeasureUnit>>();
1492        for (String type : MeasureUnit.getAvailableTypes()) {
1493            ArrayList<MeasureUnit> units = new ArrayList<MeasureUnit>(MeasureUnit.getAvailable(type));
1494            Collections.sort(
1495                    units,
1496                    new Comparator<MeasureUnit>() {
1497
1498                        public int compare(MeasureUnit o1, MeasureUnit o2) {
1499                            return o1.getSubtype().compareTo(o2.getSubtype());
1500                        }
1501
1502                    });
1503            allUnits.put(type, units);
1504        }
1505        return allUnits;
1506    }
1507
1508    // DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
1509    // for MeasureFormat during the release process.
1510    static void generateCXXConstants() {
1511        System.out.println("");
1512        TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
1513
1514        System.out.println("static const int32_t gOffsets[] = {");
1515        int index = 0;
1516        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1517            System.out.printf("    %d,\n", index);
1518            index += entry.getValue().size();
1519        }
1520        System.out.printf("    %d\n", index);
1521        System.out.println("};");
1522        System.out.println();
1523        System.out.println("static const int32_t gIndexes[] = {");
1524        index = 0;
1525        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1526            System.out.printf("    %d,\n", index);
1527            if (!entry.getKey().equals("currency")) {
1528                index += entry.getValue().size();
1529            }
1530        }
1531        System.out.printf("    %d\n", index);
1532        System.out.println("};");
1533        System.out.println();
1534        System.out.println("// Must be sorted alphabetically.");
1535        System.out.println("static const char * const gTypes[] = {");
1536        boolean first = true;
1537        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1538            if (!first) {
1539                System.out.println(",");
1540            }
1541            System.out.print("    \"" + entry.getKey() + "\"");
1542            first = false;
1543        }
1544        System.out.println();
1545        System.out.println("};");
1546        System.out.println();
1547        System.out.println("// Must be grouped by type and sorted alphabetically within each type.");
1548        System.out.println("static const char * const gSubTypes[] = {");
1549        first = true;
1550        int offset = 0;
1551        int typeIdx = 0;
1552        Map<MeasureUnit, Integer> measureUnitToOffset = new HashMap<MeasureUnit, Integer>();
1553        Map<MeasureUnit, Pair<Integer, Integer>> measureUnitToTypeSubType =
1554                new HashMap<MeasureUnit, Pair<Integer, Integer>>();
1555        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1556            int subTypeIdx = 0;
1557            for (MeasureUnit unit : entry.getValue()) {
1558                if (!first) {
1559                    System.out.println(",");
1560                }
1561                System.out.print("    \"" + unit.getSubtype() + "\"");
1562                first = false;
1563                measureUnitToOffset.put(unit, offset);
1564                measureUnitToTypeSubType.put(unit, Pair.of(typeIdx, subTypeIdx));
1565                offset++;
1566                subTypeIdx++;
1567            }
1568            typeIdx++;
1569        }
1570        System.out.println();
1571        System.out.println("};");
1572        System.out.println();
1573
1574        // Build unit per unit offsets to corresponding type sub types sorted by
1575        // unit first and then per unit.
1576        TreeMap<OrderedPair<Integer, Integer>, Pair<Integer, Integer>> unitPerUnitOffsetsToTypeSubType
1577                = new TreeMap<OrderedPair<Integer, Integer>, Pair<Integer, Integer>>();
1578        for (Map.Entry<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> entry
1579                : getUnitsToPerParts().entrySet()) {
1580            Pair<MeasureUnit, MeasureUnit> unitPerUnit = entry.getValue();
1581            unitPerUnitOffsetsToTypeSubType.put(
1582                    OrderedPair.of(
1583                            measureUnitToOffset.get(unitPerUnit.first),
1584                            measureUnitToOffset.get(unitPerUnit.second)),
1585                    measureUnitToTypeSubType.get(entry.getKey()));
1586        }
1587
1588        System.out.println("// Must be sorted by first value and then second value.");
1589        System.out.println("static int32_t unitPerUnitToSingleUnit[][4] = {");
1590        first = true;
1591        for (Map.Entry<OrderedPair<Integer, Integer>, Pair<Integer, Integer>> entry
1592                : unitPerUnitOffsetsToTypeSubType.entrySet()) {
1593            if (!first) {
1594                System.out.println(",");
1595            }
1596            first = false;
1597            OrderedPair<Integer, Integer> unitPerUnitOffsets = entry.getKey();
1598            Pair<Integer, Integer> typeSubType = entry.getValue();
1599            System.out.printf("        {%d, %d, %d, %d}",
1600                    unitPerUnitOffsets.first,
1601                    unitPerUnitOffsets.second,
1602                    typeSubType.first,
1603                    typeSubType.second);
1604        }
1605        System.out.println();
1606        System.out.println("};");
1607        System.out.println();
1608
1609        Map<String, MeasureUnit> seen = new HashMap<String, MeasureUnit>();
1610        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1611
1612            String type = entry.getKey();
1613            if (type.equals("currency")) {
1614                continue;
1615            }
1616            for (MeasureUnit unit : entry.getValue()) {
1617                String name = toCamelCase(unit);
1618                Pair<Integer, Integer> typeSubType = measureUnitToTypeSubType.get(unit);
1619                if (typeSubType == null) {
1620                    throw new IllegalStateException();
1621                }
1622                checkForDup(seen, name, unit);
1623                System.out.printf("MeasureUnit *MeasureUnit::create%s(UErrorCode &status) {\n", name);
1624                System.out.printf("    return MeasureUnit::create(%d, %d, status);\n",
1625                        typeSubType.first, typeSubType.second);
1626                System.out.println("}");
1627                System.out.println();
1628            }
1629        }
1630    }
1631
1632    private static String toCamelCase(MeasureUnit unit) {
1633        StringBuilder result = new StringBuilder();
1634        boolean caps = true;
1635        String code = unit.getSubtype();
1636        int len = code.length();
1637        for (int i = 0; i < len; i++) {
1638            char ch = code.charAt(i);
1639            if (ch == '-') {
1640                caps = true;
1641            } else if (caps) {
1642                result.append(Character.toUpperCase(ch));
1643                caps = false;
1644            } else {
1645                result.append(ch);
1646            }
1647        }
1648        return result.toString();
1649    }
1650
1651    static boolean isTypeHidden(String type) {
1652        return "currency".equals(type);
1653    }
1654
1655    // DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
1656    // for MeasureFormat during the release process.
1657    static void generateBackwardCompatibilityTest(String version) {
1658        Map<String, MeasureUnit> seen = new HashMap<String, MeasureUnit>();
1659        System.out.println();
1660        System.out.printf("    public void TestCompatible%s() {\n", version.replace(".", "_"));
1661        System.out.println("        MeasureUnit[] units = {");
1662        TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
1663        int count = 0;
1664        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1665            if (isTypeHidden(entry.getKey())) {
1666                continue;
1667            }
1668            for (MeasureUnit unit : entry.getValue()) {
1669                String javaName = toJAVAName(unit);
1670                checkForDup(seen, javaName, unit);
1671                System.out.printf("                MeasureUnit.%s,\n", javaName);
1672                count++;
1673            }
1674        }
1675        System.out.println("        };");
1676        System.out.printf("        assertEquals(\"\",  %d, units.length);\n", count);
1677        System.out.println("    }");
1678    }
1679
1680    // DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
1681    // for MeasureFormat during the release process.
1682    static void generateCXXBackwardCompatibilityTest(String version) {
1683        System.out.println();
1684        Map<String, MeasureUnit> seen = new HashMap<String, MeasureUnit>();
1685        System.out.printf("void MeasureFormatTest::TestCompatible%s() {\n", version.replace(".", "_"));
1686        System.out.println("    UErrorCode status = U_ZERO_ERROR;");
1687        System.out.println("    LocalPointer<MeasureUnit> measureUnit;");
1688        TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
1689        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1690            if (isTypeHidden(entry.getKey())) {
1691                continue;
1692            }
1693            for (MeasureUnit unit : entry.getValue()) {
1694                String camelCase = toCamelCase(unit);
1695                checkForDup(seen, camelCase, unit);
1696                System.out.printf("    measureUnit.adoptInstead(MeasureUnit::create%s(status));\n", camelCase);
1697            }
1698        }
1699        System.out.println("    assertSuccess(\"\", status);");
1700        System.out.println("}");
1701    }
1702
1703    static String toJAVAName(MeasureUnit unit) {
1704        String code = unit.getSubtype();
1705        String type = unit.getType();
1706        String name = code.toUpperCase(Locale.ENGLISH).replace("-", "_");
1707        if (type.equals("angle")) {
1708            if (code.equals("minute") || code.equals("second")) {
1709                name = "ARC_" + name;
1710            }
1711        }
1712        return name;
1713    }
1714
1715    // DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
1716    // for MeasureFormat during the release process.
1717    static void generateConstants(String thisVersion) {
1718        System.out.println();
1719        Map<String, MeasureUnit> seen = new HashMap<String, MeasureUnit>();
1720        TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
1721        for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
1722            String type = entry.getKey();
1723            if (isTypeHidden(type)) {
1724                continue;
1725            }
1726            for (MeasureUnit unit : entry.getValue()) {
1727                String name = toJAVAName(unit);
1728                String code = unit.getSubtype();
1729                checkForDup(seen, name, unit);
1730                System.out.println("    /**");
1731                System.out.println("     * Constant for unit of " + type +
1732                        ": " +
1733                        code);
1734                // Special case JAVA had old constants for time from before.
1735                if ("duration".equals(type) && TIME_CODES.contains(code)) {
1736                    System.out.println("     * @stable ICU 4.0");
1737                }
1738                else if (isDraft(name)) {
1739                    System.out.println("     * @draft ICU " + getVersion(name, thisVersion));
1740                    System.out.println("     * @provisional This API might change or be removed in a future release.");
1741                } else {
1742                    System.out.println("     * @stable ICU " + getVersion(name, thisVersion));
1743                }
1744                System.out.println("    */");
1745                if ("duration".equals(type) && TIME_CODES.contains(code)) {
1746                    System.out.println("    public static final TimeUnit " + name + " = (TimeUnit) MeasureUnit.internalGetInstance(\"" +
1747                            type +
1748                            "\", \"" +
1749                            code +
1750                            "\");");
1751                } else {
1752                    System.out.println("    public static final MeasureUnit " + name + " = MeasureUnit.internalGetInstance(\"" +
1753                            type +
1754                            "\", \"" +
1755                            code +
1756                            "\");");
1757                }
1758                System.out.println();
1759            }
1760        }
1761        System.out.println("    private static HashMap<Pair<MeasureUnit, MeasureUnit>, MeasureUnit>unitPerUnitToSingleUnit =");
1762        System.out.println("            new HashMap<Pair<MeasureUnit, MeasureUnit>, MeasureUnit>();");
1763        System.out.println();
1764        System.out.println("    static {");
1765        for (Map.Entry<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> unitPerUnitEntry
1766                : getUnitsToPerParts().entrySet()) {
1767            Pair<MeasureUnit, MeasureUnit> unitPerUnit = unitPerUnitEntry.getValue();
1768            System.out.println("        unitPerUnitToSingleUnit.put(Pair.<MeasureUnit, MeasureUnit>of(MeasureUnit." + toJAVAName(unitPerUnit.first) + ", MeasureUnit." + toJAVAName(unitPerUnit.second) + "), MeasureUnit." + toJAVAName(unitPerUnitEntry.getKey()) + ");");
1769        }
1770        System.out.println("    }");
1771    }
1772
1773    private static String getVersion(String javaName, String thisVersion) {
1774        String version = JAVA_VERSION_MAP.get(javaName);
1775        if (version == null) {
1776            return thisVersion;
1777        }
1778        return version;
1779    }
1780
1781    private static boolean isDraft(String javaName) {
1782        String version = JAVA_VERSION_MAP.get(javaName);
1783        if (version == null) {
1784            return true;
1785        }
1786        return DRAFT_VERSION_SET.contains(version);
1787    }
1788
1789    public <T extends Serializable> void checkStreamingEquality(T item) {
1790        try {
1791          ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
1792          ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOut);
1793          objectOutputStream.writeObject(item);
1794          objectOutputStream.close();
1795          byte[] contents = byteOut.toByteArray();
1796          logln("bytes: " + contents.length + "; " + item.getClass() + ": " + showBytes(contents));
1797          ByteArrayInputStream byteIn = new ByteArrayInputStream(contents);
1798          ObjectInputStream objectInputStream = new ObjectInputStream(byteIn);
1799          Object obj = objectInputStream.readObject();
1800          assertEquals("Streamed Object equals ", item, obj);
1801        } catch (IOException e) {
1802          e.printStackTrace();
1803          assertNull("Test Serialization " + item.getClass(), e);
1804        } catch (ClassNotFoundException e) {
1805          assertNull("Test Serialization " + item.getClass(), e);
1806        }
1807      }
1808
1809    /**
1810     * @param contents
1811     * @return
1812     */
1813    private String showBytes(byte[] contents) {
1814      StringBuilder b = new StringBuilder('[');
1815      for (int i = 0; i < contents.length; ++i) {
1816        int item = contents[i] & 0xFF;
1817        if (item >= 0x20 && item <= 0x7F) {
1818          b.append((char) item);
1819        } else {
1820          b.append('(').append(Utility.hex(item, 2)).append(')');
1821        }
1822      }
1823      return b.append(']').toString();
1824    }
1825
1826    private void verifyEqualsHashCode(Object o, Object eq, Object ne) {
1827        assertEquals("verifyEqualsHashCodeSame", o, o);
1828        assertEquals("verifyEqualsHashCodeEq", o, eq);
1829        assertNotEquals("verifyEqualsHashCodeNe", o, ne);
1830        assertNotEquals("verifyEqualsHashCodeEqTrans", eq, ne);
1831        assertEquals("verifyEqualsHashCodeHashEq", o.hashCode(), eq.hashCode());
1832
1833        // May be a flaky test, but generally should be true.
1834        // May need to comment this out later.
1835        assertNotEquals("verifyEqualsHashCodeHashNe", o.hashCode(), ne.hashCode());
1836    }
1837
1838    public static class MeasureUnitHandler implements SerializableTest.Handler
1839    {
1840        public Object[] getTestObjects()
1841        {
1842            MeasureUnit items[] = {
1843                    MeasureUnit.CELSIUS,
1844                    Currency.getInstance("EUR")
1845            };
1846            return items;
1847        }
1848        public boolean hasSameBehavior(Object a, Object b)
1849        {
1850            MeasureUnit a1 = (MeasureUnit) a;
1851            MeasureUnit b1 = (MeasureUnit) b;
1852            return a1.getType().equals(b1.getType())
1853                    && a1.getSubtype().equals(b1.getSubtype());
1854        }
1855    }
1856
1857    public static class MeasureFormatHandler  implements SerializableTest.Handler
1858    {
1859        public Object[] getTestObjects()
1860        {
1861            MeasureFormat items[] = {
1862                    MeasureFormat.getInstance(ULocale.FRANCE, FormatWidth.SHORT),
1863                    MeasureFormat.getInstance(
1864                            ULocale.FRANCE,
1865                            FormatWidth.WIDE,
1866                            NumberFormat.getIntegerInstance(ULocale.CANADA_FRENCH)),
1867            };
1868            return items;
1869        }
1870        public boolean hasSameBehavior(Object a, Object b)
1871        {
1872            MeasureFormat a1 = (MeasureFormat) a;
1873            MeasureFormat b1 = (MeasureFormat) b;
1874            return a1.getLocale().equals(b1.getLocale())
1875                    && a1.getWidth().equals(b1.getWidth())
1876                    && a1.getNumberFormat().equals(b1.getNumberFormat())
1877                    ;
1878        }
1879    }
1880}
1881