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