1 package com.ibm.icu.text;
2 
3 import java.util.Arrays;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Locale;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 
10 import com.ibm.icu.util.ULocale;
11 
12 /**
13  * Provide information about gender in locales based on data in CLDR. Currently just gender of lists.
14  *
15  * @author markdavis
16  */
17 public class GenderInfo {
18 
19     private final ListGenderStyle style; // set based on locale
20 
21     /**
22      * Gender: OTHER means either the information is unavailable, or the person has declined to state MALE or FEMALE.
23      */
24     public enum Gender {
25         MALE, FEMALE, OTHER
26     }
27 
28     /**
29      * Create GenderInfo from a ULocale.
30      *
31      * @param uLocale
32      */
GenderInfo(ULocale uLocale)33     public GenderInfo(ULocale uLocale) {
34         ULocale language = new ULocale(uLocale.getLanguage()); // in the hard coded data, the language is sufficient.
35         // Will change with RB.
36         ListGenderStyle tempStyle = localeToListGender.get(language);
37         style = tempStyle == null ? ListGenderStyle.NEUTRAL : tempStyle;
38     }
39 
40     /**
41      * Create GenderInfo from a Locale.
42      *
43      * @param uLocale
44      */
GenderInfo(Locale locale)45     public GenderInfo(Locale locale) {
46         this(ULocale.forLocale(locale));
47     }
48 
49     /**
50      * Enum only meant for use in CLDR and in testing. Indicates the category for the locale.
51      */
52     public enum ListGenderStyle {
53         /**
54          * Always OTHER (if more than one)
55          */
56         NEUTRAL,
57         /**
58          * gender(all male) = male, gender(all female) = female, otherwise gender(list) = other
59          */
60         MIXED_NEUTRAL,
61         /**
62          * gender(all female) = female, otherwise gender(list) = male
63          */
64         MALE_TAINTS
65     }
66 
67     /**
68      * Reset the data used for mapping locales to styles. Only for use in CLDR and in testing.
69      *
70      * @param uLocale
71      */
setLocaleMapping(Map<ULocale, ListGenderStyle> newULocaleToListGender)72     public static void setLocaleMapping(Map<ULocale, ListGenderStyle> newULocaleToListGender) {
73         localeToListGender.clear();
74         for (Entry<ULocale, ListGenderStyle> entry : newULocaleToListGender.entrySet()) {
75             localeToListGender.put(entry.getKey(), entry.getValue());
76         }
77     }
78 
79     /**
80      * Get the gender of a list, based on locale usage.
81      *
82      * @param genders
83      *            a list of genders.
84      * @return the gender of the list.
85      */
getListGender(Gender... genders)86     public Gender getListGender(Gender... genders) {
87         return getListGender(Arrays.asList(genders));
88     }
89 
90     /**
91      * Get the gender of a list, based on locale usage.
92      *
93      * @param genders
94      *            a list of genders.
95      * @return the gender of the list.
96      */
getListGender(List<Gender> genders)97     public Gender getListGender(List<Gender> genders) {
98         if (genders.size() == 0 || style == ListGenderStyle.NEUTRAL) {
99             return Gender.OTHER; // degenerate case
100         }
101         if (genders.size() == 1) {
102             return genders.get(0); // degenerate case
103         }
104         switch (style) {
105         case MIXED_NEUTRAL: // gender(all male) = male, gender(all female) = female, otherwise gender(list) = other
106             boolean hasFemale = false;
107             boolean hasMale = false;
108             for (Gender gender : genders) {
109                 switch (gender) {
110                 case FEMALE:
111                     if (hasMale) {
112                         return Gender.OTHER;
113                     }
114                     hasFemale = true;
115                     break;
116                 case MALE:
117                     if (hasFemale) {
118                         return Gender.OTHER;
119                     }
120                     hasMale = true;
121                     break;
122                 case OTHER:
123                     return Gender.OTHER;
124                 }
125             }
126             return hasMale ? Gender.MALE : hasFemale ? Gender.FEMALE : Gender.OTHER;
127         case MALE_TAINTS: // gender(all female) = female, otherwise gender(list) = male
128             for (Gender gender : genders) {
129                 if (gender != Gender.FEMALE) {
130                     return Gender.MALE;
131                 }
132             }
133             return Gender.FEMALE;
134         default:
135             return Gender.OTHER;
136         }
137     }
138 
139     // TODO Get this data from a resource bundle generated from CLDR.
140     // For now, hard coded.
141 
142     private static Map<ULocale, ListGenderStyle> localeToListGender = new HashMap<ULocale, ListGenderStyle>();
143     static {
144         for (String locale : Arrays.asList("ar", "ca", "cs", "hr", "es", "fr", "he", "hi", "it", "lt", "lv", "mr",
145             "nl", "pl", "pt", "ro", "ru", "sk", "sl", "sr", "uk", "ur", "zh")) {
localeToListGender.put(new ULocale(locale), ListGenderStyle.MALE_TAINTS)146             localeToListGender.put(new ULocale(locale), ListGenderStyle.MALE_TAINTS);
147         }
148         for (String locale : Arrays.asList("el", "is")) {
localeToListGender.put(new ULocale(locale), ListGenderStyle.MIXED_NEUTRAL)149             localeToListGender.put(new ULocale(locale), ListGenderStyle.MIXED_NEUTRAL);
150         }
151     }
152 }
153