1/*
2 ********************************************************************************
3 * Copyright (C) 2003-2014, Google, International Business Machines Corporation *
4 * and others. All Rights Reserved.                                             *
5 ********************************************************************************
6*/
7package com.ibm.icu.util;
8
9import java.util.Arrays;
10import java.util.HashMap;
11import java.util.List;
12import java.util.Locale;
13import java.util.Map;
14import java.util.MissingResourceException;
15
16import com.ibm.icu.impl.ICUCache;
17import com.ibm.icu.impl.ICUResourceBundle;
18import com.ibm.icu.impl.SimpleCache;
19
20/**
21 * Provide information about gender in locales based on data in CLDR. Currently supplies gender of lists.
22 * @author markdavis
23 * @internal
24 * @deprecated This API is ICU internal only.
25 */
26@Deprecated
27public class GenderInfo {
28
29    private final ListGenderStyle style; // set based on locale
30
31    /**
32     * Gender: OTHER means either the information is unavailable, or the person has declined to state MALE or FEMALE.
33     * @internal
34     * @deprecated This API is ICU internal only.
35     */
36    @Deprecated
37    public enum Gender {
38        /**
39         * @internal
40         * @deprecated This API is ICU internal only.
41         */
42        @Deprecated
43        MALE,
44        /**
45         * @internal
46         * @deprecated This API is ICU internal only.
47         */
48        @Deprecated
49        FEMALE,
50        /**
51         * @internal
52         * @deprecated This API is ICU internal only.
53         */
54        @Deprecated
55        OTHER
56    }
57
58    /**
59     * Create GenderInfo from a ULocale.
60     * @param uLocale desired locale
61     * @internal
62     * @deprecated This API is ICU internal only.
63     */
64    @Deprecated
65    public static GenderInfo getInstance(ULocale uLocale) {
66        return genderInfoCache.get(uLocale);
67    }
68
69    /**
70     * Create GenderInfo from a Locale.
71     * @param locale desired locale
72     * @internal
73     * @deprecated This API is ICU internal only.
74     */
75    @Deprecated
76    public static GenderInfo getInstance(Locale locale) {
77        return getInstance(ULocale.forLocale(locale));
78    }
79
80    /**
81     * Enum only meant for use in CLDR and in testing. Indicates the category for the locale.
82     * This only affects gender for lists more than one. For lists of 1 item, the gender
83     * of the list always equals the gender of that sole item.
84     * @internal
85     * @deprecated This API is ICU internal only.
86     */
87    @Deprecated
88    public enum ListGenderStyle {
89        /**
90         * For an empty list, returns OTHER;
91         * For a single item, returns its gender;
92         * Otherwise always OTHER.
93         * @internal
94         * @deprecated This API is ICU internal only.
95         */
96        @Deprecated
97        NEUTRAL,
98        /**
99         * For an empty list, returns OTHER;
100         * For a single item, returns its gender;
101         * Otherwise gender(all male) = male, gender(all female) = female, otherwise gender(list) = other.
102         * So any 'other' value makes the overall gender be 'other'.
103         * @internal
104         * @deprecated This API is ICU internal only.
105         */
106        @Deprecated
107        MIXED_NEUTRAL,
108        /**
109         * For an empty list, returns OTHER;
110         * For a single item, returns its gender;
111         * Otherwise, gender(all female) = female, otherwise gender(list) = male.
112         * So for more than one item, any 'other' value makes the overall gender be 'male'.
113         * @internal
114         * @deprecated This API is ICU internal only.
115         */
116        @Deprecated
117        MALE_TAINTS;
118
119        private static Map<String, ListGenderStyle> fromNameMap =
120            new HashMap<String, ListGenderStyle>(3);
121
122        static {
123            fromNameMap.put("neutral", NEUTRAL);
124            fromNameMap.put("maleTaints", MALE_TAINTS);
125            fromNameMap.put("mixedNeutral", MIXED_NEUTRAL);
126        }
127
128        /**
129         * @internal
130         * @deprecated This API is ICU internal only.
131         */
132        @Deprecated
133        public static ListGenderStyle fromName(String name) {
134            ListGenderStyle result = fromNameMap.get(name);
135            if (result == null) {
136                throw new IllegalArgumentException("Unknown gender style name: " + name);
137            }
138            return result;
139        }
140    }
141
142    /**
143     * Get the gender of a list, based on locale usage.
144     * @param genders a list of genders.
145     * @return the gender of the list.
146     * @internal
147     * @deprecated This API is ICU internal only.
148     */
149    @Deprecated
150    public Gender getListGender(Gender... genders) {
151        return getListGender(Arrays.asList(genders));
152    }
153
154    /**
155     * Get the gender of a list, based on locale usage.
156     * @param genders a list of genders.
157     * @return the gender of the list.
158     * @internal
159     * @deprecated This API is ICU internal only.
160     */
161    @Deprecated
162    public Gender getListGender(List<Gender> genders) {
163        if (genders.size() == 0) {
164            return Gender.OTHER; // degenerate case
165        }
166        if (genders.size() == 1) {
167            return genders.get(0); // degenerate case
168        }
169        switch(style) {
170        case NEUTRAL:
171            return Gender.OTHER;
172        case MIXED_NEUTRAL:
173            boolean hasFemale = false;
174            boolean hasMale = false;
175            for (Gender gender : genders) {
176                switch (gender) {
177                case FEMALE:
178                    if (hasMale) {
179                        return Gender.OTHER;
180                    }
181                    hasFemale = true;
182                    break;
183                case MALE:
184                    if (hasFemale) {
185                        return Gender.OTHER;
186                    }
187                    hasMale = true;
188                    break;
189                case OTHER:
190                    return Gender.OTHER;
191                }
192            }
193            return hasMale ? Gender.MALE : Gender.FEMALE;
194            // Note: any OTHER would have caused a return in the loop, which always happens.
195        case MALE_TAINTS:
196            for (Gender gender : genders) {
197                if (gender != Gender.FEMALE) {
198                    return Gender.MALE;
199                }
200            }
201            return Gender.FEMALE;
202        default:
203            return Gender.OTHER;
204        }
205    }
206
207    /**
208     * Only for testing and use with CLDR.
209     * @param genderStyle gender style
210     * @internal
211     * @deprecated This API is ICU internal only.
212     */
213    @Deprecated
214    public GenderInfo(ListGenderStyle genderStyle) {
215        style = genderStyle;
216    }
217
218    private static GenderInfo neutral = new GenderInfo(ListGenderStyle.NEUTRAL);
219
220    private static class Cache {
221        private final ICUCache<ULocale, GenderInfo> cache =
222            new SimpleCache<ULocale, GenderInfo>();
223
224        public GenderInfo get(ULocale locale) {
225            GenderInfo result = cache.get(locale);
226            if (result == null) {
227                result = load(locale);
228                if (result == null) {
229                    ULocale fallback = locale.getFallback();
230
231                    // We call get() recursively so that we can leverage the cache
232                    // for all fallback locales too. If we get to the root locale,
233                    // and find no resource assume that list gender style is NEUTRAL.
234                    result = fallback == null ? neutral : get(fallback);
235                }
236                cache.put(locale, result);
237            }
238            return result;
239        }
240
241        private static GenderInfo load(ULocale ulocale) {
242            UResourceBundle rb = UResourceBundle.getBundleInstance(
243                    ICUResourceBundle.ICU_BASE_NAME,
244                    "genderList",
245                    ICUResourceBundle.ICU_DATA_CLASS_LOADER, true);
246            UResourceBundle genderList = rb.get("genderList");
247            try {
248                return new GenderInfo(
249                        ListGenderStyle.fromName(genderList.getString(ulocale.toString())));
250            } catch (MissingResourceException mre) {
251                return null;
252            }
253        }
254    }
255
256    private static Cache genderInfoCache = new Cache();
257}
258