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 */
10package android.icu.text;
11
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.Iterator;
16import java.util.Locale;
17
18import android.icu.impl.ICUCache;
19import android.icu.impl.ICUData;
20import android.icu.impl.ICUResourceBundle;
21import android.icu.impl.SimpleCache;
22import android.icu.impl.SimpleFormatterImpl;
23import android.icu.util.ULocale;
24import android.icu.util.UResourceBundle;
25
26/**
27 * Immutable class for formatting a list, using data from CLDR (or supplied
28 * separately). The class is not subclassable.
29 *
30 * @author Mark Davis
31 */
32final public class ListFormatter {
33    // Compiled SimpleFormatter patterns.
34    private final String two;
35    private final String start;
36    private final String middle;
37    private final String end;
38    private final ULocale locale;
39
40    /**
41     * Indicates the style of Listformatter
42     * @deprecated This API is ICU internal only.
43     * @hide draft / provisional / internal are hidden on Android
44     */
45    @Deprecated
46    public enum Style {
47        /**
48         * Standard style.
49         * @deprecated This API is ICU internal only.
50         * @hide draft / provisional / internal are hidden on Android
51         */
52        @Deprecated
53        STANDARD("standard"),
54        /**
55         * Style for full durations
56         * @deprecated This API is ICU internal only.
57         * @hide draft / provisional / internal are hidden on Android
58         */
59        @Deprecated
60        DURATION("unit"),
61        /**
62         * Style for durations in abbrevated form
63         * @deprecated This API is ICU internal only.
64         * @hide draft / provisional / internal are hidden on Android
65         */
66        @Deprecated
67        DURATION_SHORT("unit-short"),
68        /**
69         * Style for durations in narrow form
70         * @deprecated This API is ICU internal only.
71         * @hide draft / provisional / internal are hidden on Android
72         */
73        @Deprecated
74        DURATION_NARROW("unit-narrow");
75
76        private final String name;
77
78        Style(String name) {
79            this.name = name;
80        }
81        /**
82         * @deprecated This API is ICU internal only.
83         * @hide draft / provisional / internal are hidden on Android
84         */
85        @Deprecated
86        public String getName() {
87            return name;
88        }
89
90    }
91
92    /**
93     * <b>Internal:</b> Create a ListFormatter from component strings,
94     * with definitions as in LDML.
95     *
96     * @param two
97     *            string for two items, containing {0} for the first, and {1}
98     *            for the second.
99     * @param start
100     *            string for the start of a list items, containing {0} for the
101     *            first, and {1} for the rest.
102     * @param middle
103     *            string for the start of a list items, containing {0} for the
104     *            first part of the list, and {1} for the rest of the list.
105     * @param end
106     *            string for the end of a list items, containing {0} for the
107     *            first part of the list, and {1} for the last item.
108     * @deprecated This API is ICU internal only.
109     * @hide draft / provisional / internal are hidden on Android
110     */
111    @Deprecated
112    public ListFormatter(String two, String start, String middle, String end) {
113        this(
114                compilePattern(two, new StringBuilder()),
115                compilePattern(start, new StringBuilder()),
116                compilePattern(middle, new StringBuilder()),
117                compilePattern(end, new StringBuilder()),
118                null);
119    }
120
121    private ListFormatter(String two, String start, String middle, String end, ULocale locale) {
122        this.two = two;
123        this.start = start;
124        this.middle = middle;
125        this.end = end;
126        this.locale = locale;
127    }
128
129    private static String compilePattern(String pattern, StringBuilder sb) {
130        return SimpleFormatterImpl.compileToStringMinMaxArguments(pattern, sb, 2, 2);
131    }
132
133    /**
134     * Create a list formatter that is appropriate for a locale.
135     *
136     * @param locale
137     *            the locale in question.
138     * @return ListFormatter
139     */
140    public static ListFormatter getInstance(ULocale locale) {
141      return getInstance(locale, Style.STANDARD);
142    }
143
144    /**
145     * Create a list formatter that is appropriate for a locale.
146     *
147     * @param locale
148     *            the locale in question.
149     * @return ListFormatter
150     */
151    public static ListFormatter getInstance(Locale locale) {
152        return getInstance(ULocale.forLocale(locale), Style.STANDARD);
153    }
154
155    /**
156     * Create a list formatter that is appropriate for a locale and style.
157     *
158     * @param locale the locale in question.
159     * @param style the style
160     * @return ListFormatter
161     * @deprecated This API is ICU internal only.
162     * @hide draft / provisional / internal are hidden on Android
163     */
164    @Deprecated
165    public static ListFormatter getInstance(ULocale locale, Style style) {
166        return cache.get(locale, style.getName());
167    }
168
169    /**
170     * Create a list formatter that is appropriate for the default FORMAT locale.
171     *
172     * @return ListFormatter
173     */
174    public static ListFormatter getInstance() {
175        return getInstance(ULocale.getDefault(ULocale.Category.FORMAT));
176    }
177
178    /**
179     * Format a list of objects.
180     *
181     * @param items
182     *            items to format. The toString() method is called on each.
183     * @return items formatted into a string
184     */
185    public String format(Object... items) {
186        return format(Arrays.asList(items));
187    }
188
189    /**
190     * Format a collection of objects. The toString() method is called on each.
191     *
192     * @param items
193     *            items to format. The toString() method is called on each.
194     * @return items formatted into a string
195     */
196    public String format(Collection<?> items) {
197        return format(items, -1).toString();
198    }
199
200    // Formats a collection of objects and returns the formatted string plus the offset
201    // in the string where the index th element appears. index is zero based. If index is
202    // negative or greater than or equal to the size of items then this function returns -1 for
203    // the offset.
204    FormattedListBuilder format(Collection<?> items, int index) {
205        Iterator<?> it = items.iterator();
206        int count = items.size();
207        switch (count) {
208        case 0:
209            return new FormattedListBuilder("", false);
210        case 1:
211            return new FormattedListBuilder(it.next(), index == 0);
212        case 2:
213            return new FormattedListBuilder(it.next(), index == 0).append(two, it.next(), index == 1);
214        }
215        FormattedListBuilder builder = new FormattedListBuilder(it.next(), index == 0);
216        builder.append(start, it.next(), index == 1);
217        for (int idx = 2; idx < count - 1; ++idx) {
218            builder.append(middle, it.next(), index == idx);
219        }
220        return builder.append(end, it.next(), index == count - 1);
221    }
222
223    /**
224     * Returns the pattern to use for a particular item count.
225     * @param count the item count.
226     * @return the pattern with {0}, {1}, {2}, etc. For English,
227     * getPatternForNumItems(3) == "{0}, {1}, and {2}"
228     * @throws IllegalArgumentException when count is 0 or negative.
229     */
230    public String getPatternForNumItems(int count) {
231        if (count <= 0) {
232            throw new IllegalArgumentException("count must be > 0");
233        }
234        ArrayList<String> list = new ArrayList<String>();
235        for (int i = 0; i < count; i++) {
236            list.add(String.format("{%d}", i));
237        }
238        return format(list);
239    }
240
241    /**
242     * Returns the locale of this object.
243     * @deprecated This API is ICU internal only.
244     * @hide draft / provisional / internal are hidden on Android
245     */
246    @Deprecated
247    public ULocale getLocale() {
248        return locale;
249    }
250
251    // Builds a formatted list
252    static class FormattedListBuilder {
253        private StringBuilder current;
254        private int offset;
255
256        // Start is the first object in the list; If recordOffset is true, records the offset of
257        // this first object.
258        public FormattedListBuilder(Object start, boolean recordOffset) {
259            this.current = new StringBuilder(start.toString());
260            this.offset = recordOffset ? 0 : -1;
261        }
262
263        // Appends additional object. pattern is a template indicating where the new object gets
264        // added in relation to the rest of the list. {0} represents the rest of the list; {1}
265        // represents the new object in pattern. next is the object to be added. If recordOffset
266        // is true, records the offset of next in the formatted string.
267        public FormattedListBuilder append(String pattern, Object next, boolean recordOffset) {
268            int[] offsets = (recordOffset || offsetRecorded()) ? new int[2] : null;
269            SimpleFormatterImpl.formatAndReplace(
270                    pattern, current, offsets, current, next.toString());
271            if (offsets != null) {
272                if (offsets[0] == -1 || offsets[1] == -1) {
273                    throw new IllegalArgumentException(
274                            "{0} or {1} missing from pattern " + pattern);
275                }
276                if (recordOffset) {
277                    offset = offsets[1];
278                } else {
279                    offset += offsets[0];
280                }
281            }
282            return this;
283        }
284
285        @Override
286        public String toString() {
287            return current.toString();
288        }
289
290        // Gets the last recorded offset or -1 if no offset recorded.
291        public int getOffset() {
292            return offset;
293        }
294
295        private boolean offsetRecorded() {
296            return offset >= 0;
297        }
298    }
299
300    private static class Cache {
301        private final ICUCache<String, ListFormatter> cache =
302            new SimpleCache<String, ListFormatter>();
303
304        public ListFormatter get(ULocale locale, String style) {
305            String key = String.format("%s:%s", locale.toString(), style);
306            ListFormatter result = cache.get(key);
307            if (result == null) {
308                result = load(locale, style);
309                cache.put(key, result);
310            }
311            return result;
312        }
313
314        private static ListFormatter load(ULocale ulocale, String style) {
315            ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
316                    getBundleInstance(ICUData.ICU_BASE_NAME, ulocale);
317            StringBuilder sb = new StringBuilder();
318            return new ListFormatter(
319                compilePattern(r.getWithFallback("listPattern/" + style + "/2").getString(), sb),
320                compilePattern(r.getWithFallback("listPattern/" + style + "/start").getString(), sb),
321                compilePattern(r.getWithFallback("listPattern/" + style + "/middle").getString(), sb),
322                compilePattern(r.getWithFallback("listPattern/" + style + "/end").getString(), sb),
323                ulocale);
324        }
325    }
326
327    static Cache cache = new Cache();
328}
329