1 package org.unicode.cldr.test; 2 3 import static java.util.Collections.disjoint; 4 5 import java.util.ArrayList; 6 import java.util.List; 7 import java.util.Set; 8 import java.util.TreeSet; 9 10 import org.unicode.cldr.tool.ToolConfig; 11 import org.unicode.cldr.util.Builder; 12 import org.unicode.cldr.util.CLDRConfig; 13 import org.unicode.cldr.util.CLDRFile; 14 import org.unicode.cldr.util.CLDRPaths; 15 import org.unicode.cldr.util.LanguageTagParser; 16 import org.unicode.cldr.util.Level; 17 import org.unicode.cldr.util.RegexLookup; 18 import org.unicode.cldr.util.RegexLookup.Finder; 19 import org.unicode.cldr.util.RegexLookup.RegexFinder; 20 import org.unicode.cldr.util.SupplementalDataInfo; 21 import org.unicode.cldr.util.SupplementalDataInfo.CoverageLevelInfo; 22 import org.unicode.cldr.util.SupplementalDataInfo.CoverageVariableInfo; 23 import org.unicode.cldr.util.Timer; 24 25 import com.ibm.icu.util.Output; 26 import com.ibm.icu.util.ULocale; 27 28 public class CoverageLevel2 { 29 30 // To modify the results, see /cldr/common/supplemental/coverageLevels.xml 31 32 /** 33 * Enable to get more verbose output when debugging 34 */ 35 private static final boolean DEBUG_LOOKUP = false; 36 37 private RegexLookup<Level> lookup = null; 38 39 enum SetMatchType { 40 Target_Language, Target_Scripts, Target_Territories, Target_TimeZones, Target_Currencies, Target_Plurals, Calendar_List 41 } 42 43 private static class LocaleSpecificInfo { 44 CoverageVariableInfo cvi; 45 String targetLanguage; 46 } 47 48 final LocaleSpecificInfo myInfo = new LocaleSpecificInfo(); 49 50 /** 51 * We define a regex finder for use in the lookup. It has extra tests based on the ci value and the cvi value, 52 * duplicating 53 * what was in SupplementalDataInfo. It uses the sets instead of converting to regex strings. 54 * 55 * @author markdavis 56 * 57 */ 58 public static class MyRegexFinder extends RegexFinder { 59 final private SetMatchType additionalMatch; 60 final private CoverageLevelInfo ci; 61 MyRegexFinder(String pattern, String additionalMatch, CoverageLevelInfo ci)62 public MyRegexFinder(String pattern, String additionalMatch, CoverageLevelInfo ci) { 63 super(pattern); 64 // remove the ${ and the }, and change - to _. 65 this.additionalMatch = additionalMatch == null 66 ? null 67 : SetMatchType.valueOf( 68 additionalMatch.substring(2, additionalMatch.length() - 1).replace('-', '_')); 69 this.ci = ci; 70 } 71 72 @Override find(String item, Object context, Info info)73 public boolean find(String item, Object context, Info info) { 74 LocaleSpecificInfo localeSpecificInfo = (LocaleSpecificInfo) context; 75 // Modified the logic to handle the case where we want specific languages and specific territories. 76 // Any match in language script or territory will succeed when multiple items are present. 77 boolean lstOK = false; 78 if (ci.inLanguage == null && ci.inScriptSet == null && ci.inTerritorySet == null) { 79 lstOK = true; 80 } else if (ci.inLanguage != null 81 && ci.inLanguage.matcher(localeSpecificInfo.targetLanguage).matches()) { 82 lstOK = true; 83 } else if (ci.inScriptSet != null 84 && !disjoint(ci.inScriptSet, localeSpecificInfo.cvi.targetScripts)) { 85 lstOK = true; 86 } else if (ci.inTerritorySet != null 87 && !disjoint(ci.inTerritorySet, localeSpecificInfo.cvi.targetTerritories)) { 88 lstOK = true; 89 } 90 91 if (!lstOK) { 92 return false; 93 } 94 boolean result = super.find(item, context, info); // also sets matcher in RegexFinder 95 if (!result) { 96 return false; 97 } 98 if (additionalMatch != null) { 99 String groupMatch = info.value[1]; 100 // String groupMatch = matcher.group(1); 101 // we match on a group, so get the right one 102 switch (additionalMatch) { 103 case Target_Language: 104 return localeSpecificInfo.targetLanguage.equals(groupMatch); 105 case Target_Scripts: 106 return localeSpecificInfo.cvi.targetScripts.contains(groupMatch); 107 case Target_Territories: 108 return localeSpecificInfo.cvi.targetTerritories.contains(groupMatch); 109 case Target_TimeZones: 110 return localeSpecificInfo.cvi.targetTimeZones.contains(groupMatch); 111 case Target_Currencies: 112 return localeSpecificInfo.cvi.targetCurrencies.contains(groupMatch); 113 // For Target_Plurals, we have to account for the fact that the @count= part might not be in the 114 // xpath, so we shouldn't reject the match because of that. ( i.e. The regex is usually 115 // ([@count='${Target-Plurals}'])? 116 case Target_Plurals: 117 return (groupMatch == null || 118 groupMatch.length() == 0 || localeSpecificInfo.cvi.targetPlurals.contains(groupMatch)); 119 case Calendar_List: 120 return localeSpecificInfo.cvi.calendars.contains(groupMatch); 121 } 122 } 123 124 return true; 125 } 126 127 @Override equals(Object obj)128 public boolean equals(Object obj) { 129 return false; 130 } 131 } 132 CoverageLevel2(SupplementalDataInfo sdi, String locale)133 private CoverageLevel2(SupplementalDataInfo sdi, String locale) { 134 myInfo.targetLanguage = new LanguageTagParser().set(locale).getLanguage(); 135 myInfo.cvi = sdi.getCoverageVariableInfo(myInfo.targetLanguage); 136 lookup = sdi.getCoverageLookup(); 137 } 138 139 /** 140 * get an instance, using CldrUtility.SUPPLEMENTAL_DIRECTORY 141 * 142 * @param locale 143 * @return 144 * @deprecated Don't use this. call the version which takes a SupplementalDataInfo as an argument. 145 * @see #getInstance(SupplementalDataInfo, String) 146 * @see CLDRPaths#SUPPLEMENTAL_DIRECTORY 147 */ 148 @Deprecated getInstance(String locale)149 public static CoverageLevel2 getInstance(String locale) { 150 return new CoverageLevel2(SupplementalDataInfo.getInstance(), locale); 151 } 152 getInstance(SupplementalDataInfo sdi, String locale)153 public static CoverageLevel2 getInstance(SupplementalDataInfo sdi, String locale) { 154 return new CoverageLevel2(sdi, locale); 155 } 156 getLevel(String path)157 public Level getLevel(String path) { 158 if (path == null) { 159 return Level.UNDETERMINED; 160 } 161 synchronized (lookup) { // synchronize on the class, since the Matchers are changed during the matching process 162 Level result; 163 if (DEBUG_LOOKUP) { // for testing 164 Output<String[]> checkItems = new Output<>(); 165 Output<Finder> matcherFound = new Output<>(); 166 List<String> failures = new ArrayList<>(); 167 result = lookup.get(path, myInfo, checkItems, matcherFound, failures); 168 for (String s : failures) { 169 System.out.println(s); 170 } 171 } else { 172 result = lookup.get(path, myInfo, null); 173 } 174 return result == null ? Level.COMPREHENSIVE : result; 175 } 176 } 177 getIntLevel(String path)178 public int getIntLevel(String path) { 179 return getLevel(path).getLevel(); 180 } 181 main(String[] args)182 public static void main(String[] args) { 183 // quick test 184 // TODO convert to unit test 185 CoverageLevel2 cv2 = CoverageLevel2.getInstance("de"); 186 ULocale uloc = new ULocale("de"); 187 CLDRConfig testInfo = ToolConfig.getToolInstance(); 188 SupplementalDataInfo supplementalDataInfo2 = testInfo.getSupplementalDataInfo(); 189 CLDRFile englishPaths1 = testInfo.getEnglish(); 190 Set<String> englishPaths = Builder.with(new TreeSet<String>()).addAll(englishPaths1).get(); 191 192 Timer timer = new Timer(); 193 timer.start(); 194 for (String path : englishPaths) { 195 int oldLevel = supplementalDataInfo2.getCoverageValueOld(path, uloc); 196 } 197 long oldTime = timer.getDuration(); 198 System.out.println(timer.toString(1)); 199 200 timer.start(); 201 for (String path : englishPaths) { 202 int newLevel = cv2.getIntLevel(path); 203 } 204 System.out.println(timer.toString(1, oldTime)); 205 206 for (String path : englishPaths) { 207 int newLevel = cv2.getIntLevel(path); 208 int oldLevel = supplementalDataInfo2.getCoverageValueOld(path, uloc); 209 if (newLevel != oldLevel) { 210 newLevel = cv2.getIntLevel(path); 211 System.out.println(oldLevel + "\t" + newLevel + "\t" + path); 212 } 213 } 214 } 215 216 } 217