xref: /aosp_15_r20/external/cldr/tools/cldr-code/src/main/java/org/unicode/cldr/util/VoterInfoList.java (revision 912701f9769bb47905792267661f0baf2b85bed5)
1 package org.unicode.cldr.util;
2 
3 import java.util.Collections;
4 import java.util.Map;
5 import java.util.Set;
6 import java.util.TreeMap;
7 import java.util.regex.Matcher;
8 import java.util.regex.Pattern;
9 import org.unicode.cldr.util.VoteResolver.Level;
10 import org.unicode.cldr.util.VoteResolver.VoterInfo;
11 
12 public class VoterInfoList {
13     /** Create a VoterInfoList with no users */
VoterInfoList()14     public VoterInfoList() {
15         clearVoterToInfo();
16     }
17 
18     /** Static info read from file */
19     private Map<Integer, VoterInfo> voterToInfo;
20 
21     private Map<String, Map<Organization, Level>> localeToOrganizationToMaxVote;
22 
getVoterToInfo()23     synchronized Map<Integer, VoterInfo> getVoterToInfo() {
24         return voterToInfo;
25     }
26 
27     /** Clear out all users. */
clearVoterToInfo()28     VoterInfoList clearVoterToInfo() {
29         setVoterToInfo(Collections.emptyMap());
30         return this;
31     }
32 
getInfoForVoter(int voter)33     public VoterInfo getInfoForVoter(int voter) {
34         return getVoterToInfo().get(voter);
35     }
36 
37     /**
38      * Set the voter info.
39      *
40      * <p>Synchronized, however, once this is called, you must NOT change the contents of your copy
41      * of newVoterToInfo. You can create a whole new one and set it.
42      */
setVoterToInfo(Map<Integer, VoterInfo> newVoterToInfo)43     public VoterInfoList setVoterToInfo(Map<Integer, VoterInfo> newVoterToInfo) {
44         computeMaxVotesAndSet(newVoterToInfo);
45         return this;
46     }
47 
48     /** Set the voter info from a users.xml file. */
setVoterToInfo(String fileName)49     public VoterInfoList setVoterToInfo(String fileName) {
50         MyHandler myHandler = new MyHandler();
51         XMLFileReader xfr = new XMLFileReader().setHandler(myHandler);
52         xfr.read(fileName, XMLFileReader.CONTENT_HANDLER | XMLFileReader.ERROR_HANDLER, false);
53         return setVoterToInfo(myHandler.testVoterToInfo);
54     }
55 
computeMaxVotesAndSet(Map<Integer, VoterInfo> newVoterToInfo)56     private void computeMaxVotesAndSet(Map<Integer, VoterInfo> newVoterToInfo) {
57         // compute the localeToOrganizationToMaxVote
58         Map<String, Map<Organization, Level>> newLocaleToOrganizationToMaxVote = new TreeMap<>();
59         for (int voter : newVoterToInfo.keySet()) {
60             VoterInfo info = newVoterToInfo.get(voter);
61             if (info.getLevel() == Level.tc || info.getLevel() == Level.locked) {
62                 continue; // skip TCs, locked
63             }
64 
65             for (CLDRLocale loc : info.getLocales()) {
66                 String locale = loc.getBaseName();
67                 Map<Organization, Level> organizationToMaxVote =
68                         newLocaleToOrganizationToMaxVote.get(locale);
69                 if (organizationToMaxVote == null) {
70                     newLocaleToOrganizationToMaxVote.put(
71                             locale, organizationToMaxVote = new TreeMap<>());
72                 }
73                 Level maxVote = organizationToMaxVote.get(info.getOrganization());
74                 if (maxVote == null || info.getLevel().compareTo(maxVote) > 0) {
75                     organizationToMaxVote.put(info.getOrganization(), info.getLevel());
76                     // System.out.println("Example best voter for " + locale + " for " +
77                     // info.organization + " is " +
78                     // info);
79                 }
80             }
81         }
82         // setters
83         synchronized (this) {
84             CldrUtility.protectCollection(newLocaleToOrganizationToMaxVote);
85             localeToOrganizationToMaxVote = newLocaleToOrganizationToMaxVote;
86             voterToInfo = Collections.unmodifiableMap(newVoterToInfo);
87         }
88     }
89 
90     /**
91      * Handles fine in xml format, turning into:
92      * //users[@host="sarasvati.unicode.org"]/user[@id="286"][@email="[email protected]"]/level[@n="1"][@type="TC"]
93      * //users[@host="sarasvati.unicode.org"]/user[@id="286"][@email="[email protected]"]/name
94      * Mike Tardif
95      * //users[@host="sarasvati.unicode.org"]/user[@id="286"][@email="[email protected]"]/org
96      * Adobe
97      * //users[@host="sarasvati.unicode.org"]/user[@id="286"][@email="[email protected]"]/locales[@type="edit"]
98      *
99      * <p>Steven's new format: //users[@generated="Wed May 07 15:57:15 PDT
100      * 2008"][@host="tintin"][@obscured="true"] /user[@id="286"][@email="?@??.??"]
101      * /level[@n="1"][@type="TC"]
102      */
103     static class MyHandler extends XMLFileReader.SimpleHandler {
104         private static final Pattern userPathMatcher =
105                 Pattern.compile(
106                         "//users(?:[^/]*)"
107                                 + "/user\\[@id=\"([^\"]*)\"](?:[^/]*)"
108                                 + "/("
109                                 + "org"
110                                 + "|name"
111                                 + "|level\\[@n=\"([^\"]*)\"]\\[@type=\"([^\"]*)\"]"
112                                 + "|locales\\[@type=\"([^\"]*)\"]"
113                                 + "(?:/locale\\[@id=\"([^\"]*)\"])?"
114                                 + ")",
115                         Pattern.COMMENTS);
116 
117         enum Group {
118             all,
119             userId,
120             mainType,
121             n,
122             levelType,
123             localeType,
124             localeId;
125 
get(Matcher matcher)126             String get(Matcher matcher) {
127                 return matcher.group(this.ordinal());
128             }
129         }
130 
131         private static final boolean DEBUG_HANDLER = false;
132         Map<Integer, VoterInfo> testVoterToInfo = new TreeMap<>();
133         Matcher matcher = userPathMatcher.matcher("");
134 
135         @Override
handlePathValue(String path, String value)136         public void handlePathValue(String path, String value) {
137             if (DEBUG_HANDLER) System.out.println(path + "\t" + value);
138             if (matcher.reset(path).matches()) {
139                 if (DEBUG_HANDLER) {
140                     for (int i = 1; i <= matcher.groupCount(); ++i) {
141                         Group group = Group.values()[i];
142                         System.out.println(i + "\t" + group + "\t" + group.get(matcher));
143                     }
144                 }
145                 int id = Integer.parseInt(Group.userId.get(matcher));
146                 VoterInfo voterInfo = testVoterToInfo.get(id);
147                 if (voterInfo == null) {
148                     testVoterToInfo.put(id, voterInfo = new VoterInfo());
149                 }
150                 final String mainType = Group.mainType.get(matcher);
151                 if (mainType.equals("org")) {
152                     Organization org = Organization.fromString(value);
153                     voterInfo.setOrganization(org);
154                     value = org.name(); // copy name back into value
155                 } else if (mainType.equals("name")) {
156                     voterInfo.setName(value);
157                 } else if (mainType.startsWith("level")) {
158                     String level = Group.levelType.get(matcher).toLowerCase();
159                     voterInfo.setLevel(Level.valueOf(level));
160                 } else if (mainType.startsWith("locale")) {
161                     final String localeIdString = Group.localeId.get(matcher);
162                     if (localeIdString != null) {
163                         CLDRLocale locale = CLDRLocale.getInstance(localeIdString.split("_")[0]);
164                         voterInfo.addLocale(locale);
165                     } else if (DEBUG_HANDLER) {
166                         System.out.println("\tskipping");
167                     }
168                 } else if (DEBUG_HANDLER) {
169                     System.out.println("\tFailed match* with " + path + "=" + value);
170                 }
171             } else {
172                 System.out.println("\tFailed match with " + path + "=" + value);
173             }
174         }
175     }
176 
getOrganizationToMaxVote(String locale)177     public Map<Organization, Level> getOrganizationToMaxVote(String locale) {
178         locale = locale.split("_")[0]; // take base language
179         Map<Organization, Level> result = localeToOrganizationToMaxVote.get(locale);
180         if (result == null) {
181             result = Collections.emptyMap();
182         }
183         return result;
184     }
185 
getOrganizationToMaxVote(Set<Integer> voters)186     public Map<Organization, Level> getOrganizationToMaxVote(Set<Integer> voters) {
187         Map<Organization, Level> orgToMaxVoteHere = new TreeMap<>();
188         for (int voter : voters) {
189             VoterInfo info = getInfoForVoter(voter);
190             if (info == null) {
191                 continue; // skip unknown voter
192             }
193             Level maxVote = orgToMaxVoteHere.get(info.getOrganization());
194             if (maxVote == null || info.getLevel().compareTo(maxVote) > 0) {
195                 orgToMaxVoteHere.put(info.getOrganization(), info.getLevel());
196                 // System.out.println("*Best voter for " + info.organization + " is " + info);
197             }
198         }
199         return orgToMaxVoteHere;
200     }
201 
get(int voter)202     public VoterInfo get(int voter) {
203         return voterToInfo.get(voter);
204     }
205 }
206