• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2016 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 /*
5  *******************************************************************************
6  * Copyright (C) 2012-2016, Google, International Business Machines Corporation and
7  * others. All Rights Reserved.
8  *******************************************************************************
9  */
10 package ohos.global.icu.text;
11 
12 import java.io.InvalidObjectException;
13 import java.text.AttributedCharacterIterator;
14 import java.text.Format;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Iterator;
19 import java.util.Locale;
20 import java.util.regex.Pattern;
21 
22 import ohos.global.icu.impl.FormattedStringBuilder;
23 import ohos.global.icu.impl.FormattedValueStringBuilderImpl;
24 import ohos.global.icu.impl.FormattedValueStringBuilderImpl.SpanFieldPlaceholder;
25 import ohos.global.icu.impl.ICUCache;
26 import ohos.global.icu.impl.ICUData;
27 import ohos.global.icu.impl.ICUResourceBundle;
28 import ohos.global.icu.impl.SimpleCache;
29 import ohos.global.icu.impl.SimpleFormatterImpl;
30 import ohos.global.icu.impl.SimpleFormatterImpl.IterInternal;
31 import ohos.global.icu.impl.Utility;
32 import ohos.global.icu.util.ULocale;
33 import ohos.global.icu.util.UResourceBundle;
34 
35 /**
36  * Immutable class for formatting a list, using data from CLDR (or supplied
37  * separately). The class is not subclassable.
38  *
39  * @author Mark Davis
40  */
41 final public class ListFormatter {
42     // Compiled SimpleFormatter patterns.
43     private final String start;
44     private final String middle;
45     private final ULocale locale;
46 
47     private interface PatternHandler {
getTwoPattern(String text)48         public String getTwoPattern(String text);
getEndPattern(String text)49         public String getEndPattern(String text);
50     }
51     private final PatternHandler patternHandler;
52 
53     /**
54      * Indicates the style of Listformatter
55      * TODO(ICU-20888): Remove this in ICU 68.
56      * @deprecated This API is ICU internal only.
57      * @hide exposed on OHOS
58      * @hide draft / provisional / internal are hidden on OHOS
59      */
60     @Deprecated
61     public enum Style {
62         /**
63          * Standard, conjunction style.
64          * @deprecated This API is ICU internal only.
65          * @hide draft / provisional / internal are hidden on OHOS
66          */
67         @Deprecated
68         STANDARD("standard"),
69         /**
70          * Disjunction style.
71          * @deprecated This API is ICU internal only.
72          * @hide draft / provisional / internal are hidden on OHOS
73          */
74         @Deprecated
75         OR("or"),
76         /**
77          * Style for full units
78          * @deprecated This API is ICU internal only.
79          * @hide draft / provisional / internal are hidden on OHOS
80          */
81         @Deprecated
82         UNIT("unit"),
83         /**
84          * Style for units in abbrevated form
85          * @deprecated This API is ICU internal only.
86          * @hide draft / provisional / internal are hidden on OHOS
87          */
88         @Deprecated
89         UNIT_SHORT("unit-short"),
90         /**
91          * Style for units in narrow form
92          * @deprecated This API is ICU internal only.
93          * @hide draft / provisional / internal are hidden on OHOS
94          */
95         @Deprecated
96         UNIT_NARROW("unit-narrow");
97 
98         private final String name;
99 
Style(String name)100         Style(String name) {
101             this.name = name;
102         }
103         /**
104          * @deprecated This API is ICU internal only.
105          * @hide draft / provisional / internal are hidden on OHOS
106          */
107         @Deprecated
getName()108         public String getName() {
109             return name;
110         }
111 
112     }
113 
114     /**
115      * Type of meaning expressed by the list.
116      *
117      * @hide exposed on OHOS
118      * @hide draft / provisional / internal are hidden on OHOS
119      */
120     public enum Type {
121         /**
122          * Conjunction formatting, e.g. "Alice, Bob, Charlie, and Delta".
123          *
124          * @hide draft / provisional / internal are hidden on OHOS
125          */
126         AND,
127 
128         /**
129          * Disjunction (or alternative, or simply one of) formatting, e.g.
130          * "Alice, Bob, Charlie, or Delta".
131          *
132          * @hide draft / provisional / internal are hidden on OHOS
133          */
134         OR,
135 
136         /**
137          * Formatting of a list of values with units, e.g. "5 pounds, 12 ounces".
138          *
139          * @hide draft / provisional / internal are hidden on OHOS
140          */
141         UNITS
142     };
143 
144     /**
145      * Verbosity level of the list patterns.
146      *
147      * @hide exposed on OHOS
148      * @hide draft / provisional / internal are hidden on OHOS
149      */
150     public enum Width {
151         /**
152          * Use list formatting with full words (no abbreviations) when possible.
153          *
154          * @hide draft / provisional / internal are hidden on OHOS
155          */
156         WIDE,
157 
158         /**
159          * Use list formatting of typical length.
160          *
161          * @hide draft / provisional / internal are hidden on OHOS
162          */
163         SHORT,
164 
165         /**
166          * Use list formatting of the shortest possible length.
167          *
168          * @hide draft / provisional / internal are hidden on OHOS
169          */
170         NARROW,
171     };
172 
173     /**
174      * Class for span fields in FormattedList.
175      *
176      * @hide exposed on OHOS
177      * @hide draft / provisional / internal are hidden on OHOS
178      */
179     public static final class SpanField extends UFormat.SpanField {
180         private static final long serialVersionUID = 3563544214705634403L;
181 
182         /**
183          * The concrete field used for spans in FormattedList.
184          *
185          * Instances of LIST_SPAN should have an associated value, the index
186          * within the input list that is represented by the span.
187          *
188          * @hide draft / provisional / internal are hidden on OHOS
189          */
190         public static final SpanField LIST_SPAN = new SpanField("list-span");
191 
SpanField(String name)192         private SpanField(String name) {
193             super(name);
194         }
195 
196         /**
197          * serialization method resolve instances to the constant
198          * ListFormatter.SpanField values
199          * @deprecated This API is ICU internal only.
200          * @hide draft / provisional / internal are hidden on OHOS
201          */
202         @Deprecated
203         @Override
readResolve()204         protected Object readResolve() throws InvalidObjectException {
205             if (this.getName().equals(LIST_SPAN.getName()))
206                 return LIST_SPAN;
207 
208             throw new InvalidObjectException("An invalid object.");
209         }
210     }
211 
212     /**
213      * Field selectors for format fields defined by ListFormatter.
214      * @hide exposed on OHOS
215      * @hide draft / provisional / internal are hidden on OHOS
216      */
217     public static final class Field extends Format.Field {
218         private static final long serialVersionUID = -8071145668708265437L;
219 
220         /**
221          * The literal text in the result which came from the resources.
222          * @hide draft / provisional / internal are hidden on OHOS
223          */
224         public static Field LITERAL = new Field("literal");
225 
226         /**
227          * The element text in the result which came from the input strings.
228          * @hide draft / provisional / internal are hidden on OHOS
229          */
230         public static Field ELEMENT = new Field("element");
231 
Field(String name)232         private Field(String name) {
233             super(name);
234         }
235 
236         /**
237          * Serizalization method resolve instances to the constant Field values
238          *
239          * @hide draft / provisional / internal are hidden on OHOS
240          */
241         @Override
readResolve()242         protected Object readResolve() throws InvalidObjectException {
243             if (this.getName().equals(LITERAL.getName()))
244                 return LITERAL;
245             if (this.getName().equals(ELEMENT.getName()))
246                 return ELEMENT;
247 
248             throw new InvalidObjectException("An invalid object.");
249         }
250     }
251 
252     /**
253      * An immutable class containing the result of a list formatting operation.
254      *
255      * Instances of this class are immutable and thread-safe.
256      *
257      * Not intended for public subclassing.
258      *
259      * @hide exposed on OHOS
260      * @hide draft / provisional / internal are hidden on OHOS
261      */
262     public static final class FormattedList implements FormattedValue {
263         private final FormattedStringBuilder string;
264 
FormattedList(FormattedStringBuilder string)265         FormattedList(FormattedStringBuilder string) {
266             this.string = string;
267         }
268 
269         /**
270          * {@inheritDoc}
271          * @hide draft / provisional / internal are hidden on OHOS
272          */
273         @Override
toString()274         public String toString() {
275             return string.toString();
276         }
277 
278         /**
279          * {@inheritDoc}
280          * @hide draft / provisional / internal are hidden on OHOS
281          */
282         @Override
length()283         public int length() {
284             return string.length();
285         }
286 
287         /**
288          * {@inheritDoc}
289          * @hide draft / provisional / internal are hidden on OHOS
290          */
291         @Override
charAt(int index)292         public char charAt(int index) {
293             return string.charAt(index);
294         }
295 
296         /**
297          * {@inheritDoc}
298          * @hide draft / provisional / internal are hidden on OHOS
299          */
300         @Override
subSequence(int start, int end)301         public CharSequence subSequence(int start, int end) {
302             return string.subString(start, end);
303         }
304 
305         /**
306          * {@inheritDoc}
307          * @hide draft / provisional / internal are hidden on OHOS
308          */
309         @Override
appendTo(A appendable)310         public <A extends Appendable> A appendTo(A appendable) {
311             return Utility.appendTo(string, appendable);
312         }
313 
314         /**
315          * {@inheritDoc}
316          * @hide draft / provisional / internal are hidden on OHOS
317          */
318         @Override
nextPosition(ConstrainedFieldPosition cfpos)319         public boolean nextPosition(ConstrainedFieldPosition cfpos) {
320             return FormattedValueStringBuilderImpl.nextPosition(string, cfpos, null);
321         }
322 
323         /**
324          * {@inheritDoc}
325          * @hide draft / provisional / internal are hidden on OHOS
326          */
327         @Override
toCharacterIterator()328         public AttributedCharacterIterator toCharacterIterator() {
329             return FormattedValueStringBuilderImpl.toCharacterIterator(string, null);
330         }
331     }
332 
333     /**
334      * <b>Internal:</b> Create a ListFormatter from component strings,
335      * with definitions as in LDML.
336      *
337      * @param two
338      *            string for two items, containing {0} for the first, and {1}
339      *            for the second.
340      * @param start
341      *            string for the start of a list items, containing {0} for the
342      *            first, and {1} for the rest.
343      * @param middle
344      *            string for the start of a list items, containing {0} for the
345      *            first part of the list, and {1} for the rest of the list.
346      * @param end
347      *            string for the end of a list items, containing {0} for the
348      *            first part of the list, and {1} for the last item.
349      * @deprecated This API is ICU internal only.
350      * @hide draft / provisional / internal are hidden on OHOS
351      */
352     @Deprecated
ListFormatter(String two, String start, String middle, String end)353     public ListFormatter(String two, String start, String middle, String end) {
354         this(
355                 compilePattern(two, new StringBuilder()),
356                 compilePattern(start, new StringBuilder()),
357                 compilePattern(middle, new StringBuilder()),
358                 compilePattern(end, new StringBuilder()),
359                 null);
360     }
361 
ListFormatter(String two, String start, String middle, String end, ULocale locale)362     private ListFormatter(String two, String start, String middle, String end, ULocale locale) {
363         this.start = start;
364         this.middle = middle;
365         this.locale = locale;
366         this.patternHandler = createPatternHandler(two, end);
367     }
368 
compilePattern(String pattern, StringBuilder sb)369     private static String compilePattern(String pattern, StringBuilder sb) {
370         return SimpleFormatterImpl.compileToStringMinMaxArguments(pattern, sb, 2, 2);
371     }
372 
373     /**
374      * Create a list formatter that is appropriate for a locale.
375      *
376      * @param locale
377      *            the locale in question.
378      * @return ListFormatter
379      * @hide draft / provisional / internal are hidden on OHOS
380      */
getInstance(ULocale locale, Type type, Width width)381     public static ListFormatter getInstance(ULocale locale, Type type, Width width) {
382         String styleName = typeWidthToStyleString(type, width);
383         if (styleName == null) {
384             throw new IllegalArgumentException("Invalid list format type/width");
385         }
386         return cache.get(locale, styleName);
387     }
388 
389     /**
390      * Create a list formatter that is appropriate for a locale.
391      *
392      * @param locale
393      *            the locale in question.
394      * @return ListFormatter
395      * @hide draft / provisional / internal are hidden on OHOS
396      */
getInstance(Locale locale, Type type, Width width)397     public static ListFormatter getInstance(Locale locale, Type type, Width width) {
398         return getInstance(ULocale.forLocale(locale), type, width);
399     }
400 
401     /**
402      * Create a list formatter that is appropriate for a locale and style.
403      *
404      * @param locale the locale in question.
405      * @param style the style
406      * @return ListFormatter
407      * @deprecated This API is ICU internal only.
408      * @hide draft / provisional / internal are hidden on OHOS
409      */
410     @Deprecated
getInstance(ULocale locale, Style style)411     public static ListFormatter getInstance(ULocale locale, Style style) {
412         return cache.get(locale, style.getName());
413     }
414 
415     /**
416      * Create a list formatter that is appropriate for a locale.
417      *
418      * @param locale
419      *            the locale in question.
420      * @return ListFormatter
421      */
getInstance(ULocale locale)422     public static ListFormatter getInstance(ULocale locale) {
423       return getInstance(locale, Style.STANDARD);
424     }
425 
426     /**
427      * Create a list formatter that is appropriate for a locale.
428      *
429      * @param locale
430      *            the locale in question.
431      * @return ListFormatter
432      */
getInstance(Locale locale)433     public static ListFormatter getInstance(Locale locale) {
434         return getInstance(ULocale.forLocale(locale), Style.STANDARD);
435     }
436 
437     /**
438      * Create a list formatter that is appropriate for the default FORMAT locale.
439      *
440      * @return ListFormatter
441      */
getInstance()442     public static ListFormatter getInstance() {
443         return getInstance(ULocale.getDefault(ULocale.Category.FORMAT));
444     }
445 
446     /**
447      * Format a list of objects.
448      *
449      * @param items
450      *            items to format. The toString() method is called on each.
451      * @return items formatted into a string
452      */
format(Object... items)453     public String format(Object... items) {
454         return format(Arrays.asList(items));
455     }
456 
457     /**
458      * Format a collection of objects. The toString() method is called on each.
459      *
460      * @param items
461      *            items to format. The toString() method is called on each.
462      * @return items formatted into a string
463      */
format(Collection<?> items)464     public String format(Collection<?> items) {
465         return formatImpl(items, false).toString();
466     }
467 
468     /**
469      * Format a list of objects to a FormattedList. You can access the offsets
470      * of each element from the FormattedList.
471      *
472      * @param items
473      *            items to format. The toString() method is called on each.
474      * @return items formatted into a FormattedList
475      * @hide draft / provisional / internal are hidden on OHOS
476      */
formatToValue(Object... items)477     public FormattedList formatToValue(Object... items) {
478         return formatToValue(Arrays.asList(items));
479     }
480 
481 
482     /**
483      * Format a collection of objects to a FormattedList. You can access the offsets
484      * of each element from the FormattedList.
485      *
486      * @param items
487      *            items to format. The toString() method is called on each.
488      * @return items formatted into a FormattedList
489      * @hide draft / provisional / internal are hidden on OHOS
490      */
formatToValue(Collection<?> items)491     public FormattedList formatToValue(Collection<?> items) {
492         return formatImpl(items, true).toValue();
493     }
494 
495     // Formats a collection of objects and returns the formatted string plus the offset
496     // in the string where the index th element appears. index is zero based. If index is
497     // negative or greater than or equal to the size of items then this function returns -1 for
498     // the offset.
formatImpl(Collection<?> items, boolean needsFields)499     FormattedListBuilder formatImpl(Collection<?> items, boolean needsFields) {
500         Iterator<?> it = items.iterator();
501         int count = items.size();
502         switch (count) {
503         case 0:
504             return new FormattedListBuilder("", needsFields);
505         case 1:
506             return new FormattedListBuilder(it.next(), needsFields);
507         case 2:
508             Object first = it.next();
509             Object second = it.next();
510             return new FormattedListBuilder(first, needsFields)
511                 .append(patternHandler.getTwoPattern(String.valueOf(second)), second, 1);
512         }
513         FormattedListBuilder builder = new FormattedListBuilder(it.next(), needsFields);
514         builder.append(start, it.next(), 1);
515         for (int idx = 2; idx < count - 1; ++idx) {
516             builder.append(middle, it.next(), idx);
517         }
518         Object last = it.next();
519         return builder.append(patternHandler.getEndPattern(String.valueOf(last)), last, count - 1);
520     }
521 
522     // A static handler just returns the pattern without considering the input text.
523     private class StaticHandler implements PatternHandler {
StaticHandler(String two, String end)524         StaticHandler(String two, String end) {
525             twoPattern = two;
526             endPattern = end;
527         }
528 
529         @Override
getTwoPattern(String text)530         public String getTwoPattern(String text) { return twoPattern; }
531 
532         @Override
getEndPattern(String text)533         public String getEndPattern(String text) { return endPattern; }
534 
535         private final String twoPattern;
536         private final String endPattern;
537     }
538 
539     // A contextual handler returns one of the two patterns depending on whether the text matched the regexp.
540     private class ContextualHandler implements PatternHandler {
ContextualHandler(Pattern regexp, String thenTwo, String elseTwo, String thenEnd, String elseEnd)541         ContextualHandler(Pattern regexp, String thenTwo, String elseTwo, String thenEnd, String elseEnd) {
542             this.regexp = regexp;
543             thenTwoPattern = thenTwo;
544             elseTwoPattern = elseTwo;
545             thenEndPattern = thenEnd;
546             elseEndPattern = elseEnd;
547         }
548 
549         @Override
getTwoPattern(String text)550         public String getTwoPattern(String text) {
551             if(regexp.matcher(text).matches()) {
552                 return thenTwoPattern;
553             } else {
554                 return elseTwoPattern;
555             }
556         }
557 
558         @Override
getEndPattern(String text)559         public String getEndPattern(String text) {
560             if(regexp.matcher(text).matches()) {
561                 return thenEndPattern;
562             } else {
563                 return elseEndPattern;
564             }
565         }
566 
567         private final Pattern regexp;
568         private final String thenTwoPattern;
569         private final String elseTwoPattern;
570         private final String thenEndPattern;
571         private final String elseEndPattern;
572 
573     }
574 
575     // Pattern in the ICU Data which might be replaced y by e.
576     private static final String compiledY = compilePattern("{0} y {1}", new StringBuilder());
577 
578     // The new pattern to replace y to e
579     private static final String compiledE = compilePattern("{0} e {1}", new StringBuilder());
580 
581     // Pattern in the ICU Data which might be replaced o by u.
582     private static final String compiledO = compilePattern("{0} o {1}", new StringBuilder());
583 
584     // The new pattern to replace u to o
585     private static final String compiledU = compilePattern("{0} u {1}", new StringBuilder());
586 
587     // Condition to change to e.
588     // Starts with "hi" or "i" but not with "hie" nor "hia"a
589     private static final Pattern changeToE = Pattern.compile("(i.*|hi|hi[^ae].*)", Pattern.CASE_INSENSITIVE);
590 
591     // Condition to change to u.
592     // Starts with "o", "ho", and "8". Also "11" by itself.
593     private static final Pattern changeToU = Pattern.compile("((o|ho|8).*|11)", Pattern.CASE_INSENSITIVE);
594 
595     // Pattern in the ICU Data which might need to add a DASH after VAV.
596     private static final String compiledVav = compilePattern("{0} \u05D5{1}", new StringBuilder());
597 
598     // Pattern to add a DASH after VAV.
599     private static final String compiledVavDash = compilePattern("{0} \u05D5-{1}", new StringBuilder());
600 
601     // Condition to change to VAV follow by a dash.
602     // Starts with non Hebrew letter.
603     private static final Pattern changeToVavDash = Pattern.compile("^[\\P{InHebrew}].*$");
604 
605     // A factory function to create function based on locale
606     // Handle specal case of Spanish and Hebrew
createPatternHandler(String two, String end)607     private PatternHandler createPatternHandler(String two, String end) {
608         if (this.locale != null) {
609             String language = this.locale.getLanguage();
610             if (language.equals("es")) {
611                 boolean twoIsY = two.equals(compiledY);
612                 boolean endIsY = end.equals(compiledY);
613                 if (twoIsY || endIsY) {
614                     return new ContextualHandler(
615                         changeToE, twoIsY ? compiledE : two, two, endIsY ? compiledE : end, end);
616                 }
617                 boolean twoIsO = two.equals(compiledO);
618                 boolean endIsO = end.equals(compiledO);
619                 if (twoIsO || endIsO) {
620                     return new ContextualHandler(
621                         changeToU, twoIsO ? compiledU : two, two, endIsO ? compiledU : end, end);
622                 }
623             } else if (language.equals("he") || language.equals("iw")) {
624                 boolean twoIsVav = two.equals(compiledVav);
625                 boolean endIsVav = end.equals(compiledVav);
626                 if (twoIsVav || endIsVav) {
627                     return new ContextualHandler(changeToVavDash,
628                         twoIsVav ? compiledVavDash : two, two, endIsVav ? compiledVavDash : end, end);
629                 }
630             }
631         }
632         return new StaticHandler(two, end);
633     }
634 
635     /**
636      * Returns the pattern to use for a particular item count.
637      * @param count the item count.
638      * @return the pattern with {0}, {1}, {2}, etc. For English,
639      * getPatternForNumItems(3) == "{0}, {1}, and {2}"
640      * @throws IllegalArgumentException when count is 0 or negative.
641      */
getPatternForNumItems(int count)642     public String getPatternForNumItems(int count) {
643         if (count <= 0) {
644             throw new IllegalArgumentException("count must be > 0");
645         }
646         ArrayList<String> list = new ArrayList<>();
647         for (int i = 0; i < count; i++) {
648             list.add(String.format("{%d}", i));
649         }
650         return format(list);
651     }
652 
653     /**
654      * Returns the locale of this object.
655      * @deprecated This API is ICU internal only.
656      * @hide draft / provisional / internal are hidden on OHOS
657      */
658     @Deprecated
getLocale()659     public ULocale getLocale() {
660         return locale;
661     }
662 
663     // Builds a formatted list
664     static class FormattedListBuilder {
665         private FormattedStringBuilder string;
666         boolean needsFields;
667 
668         // Start is the first object in the list; If needsFields is true, enable the slightly
669         // more expensive code path that records offsets of each element.
FormattedListBuilder(Object start, boolean needsFields)670         public FormattedListBuilder(Object start, boolean needsFields) {
671             string = new FormattedStringBuilder();
672             this.needsFields = needsFields;
673             string.setAppendableField(Field.LITERAL);
674             appendElement(start, 0);
675         }
676 
677         // Appends additional object. pattern is a template indicating where the new object gets
678         // added in relation to the rest of the list. {0} represents the rest of the list; {1}
679         // represents the new object in pattern. next is the object to be added. position is the
680         // index of the next object in the list of inputs.
append(String compiledPattern, Object next, int position)681         public FormattedListBuilder append(String compiledPattern, Object next, int position) {
682             assert SimpleFormatterImpl.getArgumentLimit(compiledPattern) == 2;
683             string.setAppendIndex(0);
684             long state = 0;
685             while (true) {
686                 state = IterInternal.step(state, compiledPattern, string);
687                 if (state == IterInternal.DONE) {
688                     break;
689                 }
690                 int argIndex = IterInternal.getArgIndex(state);
691                 if (argIndex == 0) {
692                     string.setAppendIndex(string.length());
693                 } else {
694                     appendElement(next, position);
695                 }
696             }
697             return this;
698         }
699 
appendElement(Object element, int position)700         private void appendElement(Object element, int position) {
701             if (needsFields) {
702                 SpanFieldPlaceholder field = new SpanFieldPlaceholder();
703                 field.spanField = SpanField.LIST_SPAN;
704                 field.normalField = Field.ELEMENT;
705                 field.value = position;
706                 string.append(element.toString(), field);
707             } else {
708                 string.append(element.toString(), null);
709             }
710         }
711 
appendTo(Appendable appendable)712         public void appendTo(Appendable appendable) {
713             Utility.appendTo(string, appendable);
714         }
715 
getOffset(int fieldPositionFoundIndex)716         public int getOffset(int fieldPositionFoundIndex) {
717             return FormattedValueStringBuilderImpl.findSpan(string, fieldPositionFoundIndex);
718         }
719 
720         @Override
toString()721         public String toString() {
722             return string.toString();
723         }
724 
toValue()725         public FormattedList toValue() {
726             return new FormattedList(string);
727         }
728     }
729 
730     private static class Cache {
731         private final ICUCache<String, ListFormatter> cache =
732             new SimpleCache<>();
733 
get(ULocale locale, String style)734         public ListFormatter get(ULocale locale, String style) {
735             String key = String.format("%s:%s", locale.toString(), style);
736             ListFormatter result = cache.get(key);
737             if (result == null) {
738                 result = load(locale, style);
739                 cache.put(key, result);
740             }
741             return result;
742         }
743 
load(ULocale ulocale, String style)744         private static ListFormatter load(ULocale ulocale, String style) {
745             ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
746                     getBundleInstance(ICUData.ICU_BASE_NAME, ulocale);
747             StringBuilder sb = new StringBuilder();
748             return new ListFormatter(
749                 compilePattern(r.getWithFallback("listPattern/" + style + "/2").getString(), sb),
750                 compilePattern(r.getWithFallback("listPattern/" + style + "/start").getString(), sb),
751                 compilePattern(r.getWithFallback("listPattern/" + style + "/middle").getString(), sb),
752                 compilePattern(r.getWithFallback("listPattern/" + style + "/end").getString(), sb),
753                 ulocale);
754         }
755     }
756 
757     static Cache cache = new Cache();
758 
typeWidthToStyleString(Type type, Width width)759     static String typeWidthToStyleString(Type type, Width width) {
760         switch (type) {
761             case AND:
762                 switch (width) {
763                     case WIDE:
764                         return "standard";
765                     case SHORT:
766                         return "standard-short";
767                     case NARROW:
768                         return "standard-narrow";
769                 }
770                 break;
771 
772             case OR:
773                 switch (width) {
774                     case WIDE:
775                         return "or";
776                     case SHORT:
777                         return "or-short";
778                     case NARROW:
779                         return "or-narrow";
780                 }
781                 break;
782 
783             case UNITS:
784                 switch (width) {
785                     case WIDE:
786                         return "unit";
787                     case SHORT:
788                         return "unit-short";
789                     case NARROW:
790                         return "unit-narrow";
791                 }
792         }
793 
794         return null;
795     }
796 }
797