1 package org.unicode.cldr.util; 2 3 import com.google.common.collect.Multimap; 4 import com.ibm.icu.impl.Relation; 5 import com.ibm.icu.util.TimeZone; 6 import java.util.ArrayList; 7 import java.util.Collection; 8 import java.util.Collections; 9 import java.util.HashMap; 10 import java.util.HashSet; 11 import java.util.LinkedHashMap; 12 import java.util.LinkedHashSet; 13 import java.util.LinkedList; 14 import java.util.List; 15 import java.util.Map; 16 import java.util.Set; 17 18 public class Containment { 19 private static final SupplementalDataInfo supplementalData = SupplementalDataInfo.getInstance(); 20 static final Relation<String, String> containmentCore = supplementalData.getContainmentCore(); 21 static final Set<String> continents = containmentCore.get("001"); 22 static final Set<String> subcontinents; 23 24 static { 25 LinkedHashSet<String> temp = new LinkedHashSet<>(); 26 for (String continent : continents) { containmentCore.get(continent)27 temp.addAll(containmentCore.get(continent)); 28 } 29 subcontinents = Collections.unmodifiableSet(temp); 30 } 31 32 static final Relation<String, String> containmentFull = 33 supplementalData.getTerritoryToContained(); 34 static final Relation<String, String> containedToContainer = 35 Relation.of(new HashMap<String, Set<String>>(), HashSet.class) 36 .addAllInverted(containmentFull) 37 .freeze(); 38 39 static final Relation<String, String> leavesToContainers; 40 41 static { 42 leavesToContainers = Relation.of(new HashMap<String, Set<String>>(), HashSet.class); 43 // for each container, get all of its leaf nodes 44 Set<String> containers = supplementalData.getContainers(); 45 for (String s : containers) { 46 HashSet<String> leaves = new HashSet<>(); addLeaves(s, leaves, containers)47 addLeaves(s, leaves, containers); leavesToContainers.putAll(leaves, s)48 leavesToContainers.putAll(leaves, s); 49 } leavesToContainers.freeze()50 leavesToContainers.freeze(); 51 // for (Entry<String, Set<String>> e : leavesToContainers.keyValuesSet()) { 52 // System.out.println(e.getKey() + " " + e.getValue()); 53 // } 54 } 55 56 static final Relation<String, String> containedToContainerCore = 57 Relation.of(new HashMap<String, Set<String>>(), HashSet.class) 58 .addAllInverted(containmentCore) 59 .freeze(); 60 static final Map<String, Integer> toOrder = new LinkedHashMap<>(); 61 static int level = 0; 62 static int order; 63 /** track whether any errors, such as loops, were detected */ 64 public static boolean hadErrors = false; 65 66 static { 67 initOrder("001"); 68 // Special cases. Cyprus is because it is in the wrong location because it gets picked up in 69 // the EU. 70 resetOrder("003", "021"); 71 resetOrder("419", "005"); 72 resetOrder("CY", "BH"); 73 } 74 75 // static Map<String, String> zone2country = StandardCodes.make().getZoneToCounty(); 76 getRegionFromZone(String tzid)77 public static String getRegionFromZone(String tzid) { 78 if ("Etc/Unknown".equals(tzid)) { 79 return "001"; 80 } 81 try { 82 return TimeZone.getRegion(tzid); 83 } catch (IllegalArgumentException e) { 84 return "ZZ"; 85 } 86 // return zone2country.get(source0); 87 } 88 addLeaves(String s, Set<String> target, Set<String> nonLeaf)89 private static void addLeaves(String s, Set<String> target, Set<String> nonLeaf) { 90 Set<String> contained = supplementalData.getContained(s); 91 if (contained == null) { 92 return; 93 } 94 for (String child : contained) { 95 if (!nonLeaf.contains(child)) { 96 target.add(child); 97 } else { 98 addLeaves(child, target, nonLeaf); 99 } 100 } 101 } 102 getContainer(String territory)103 public static String getContainer(String territory) { 104 Set<String> containers = containedToContainerCore.get(territory); 105 if (containers == null) { 106 containers = containedToContainer.get(territory); 107 } 108 String container = 109 containers != null 110 ? containers.iterator().next() 111 : territory.equals("001") ? "001" : "ZZ"; 112 return container; 113 } 114 115 /** 116 * Return all the containers, including deprecated. 117 * 118 * @param territory 119 * @return 120 */ getContainers(String territory)121 public static Set<String> getContainers(String territory) { 122 return containedToContainer.get(territory); 123 } 124 125 /** 126 * Return the Continent containing the territory, or 001 if the territory is 001, otherwise ZZ 127 * 128 * @param territory 129 */ getContinent(String territory)130 public static String getContinent(String territory) { 131 while (true) { 132 if (territory == null 133 || territory.equals("001") 134 || territory.equals("ZZ") 135 || continents.contains(territory)) { 136 return territory; 137 } 138 String newTerritory = getContainer(territory); 139 if (newTerritory == null) { 140 return territory; 141 } 142 territory = newTerritory; 143 } 144 } 145 146 /** 147 * Return the Subcontinent containing the territory, or the continent if it is a continent, or 148 * 001 if it is 001, otherwise ZZ. 149 * 150 * @param territory 151 */ getSubcontinent(String territory)152 public static String getSubcontinent(String territory) { 153 while (true) { 154 if (territory.equals("001") 155 || territory.equals("ZZ") 156 || continents.contains(territory) 157 || subcontinents.contains(territory)) { 158 return territory; 159 } 160 territory = getContainer(territory); 161 } 162 } 163 getOrder(String territory)164 public static int getOrder(String territory) { 165 Integer temp = toOrder.get(territory); 166 return temp != null ? temp.intValue() : level; 167 } 168 initOrder(String territory)169 private static void initOrder(String territory) { 170 if (!toOrder.containsKey(territory)) { 171 toOrder.put(territory, ++level); 172 } 173 Set<String> contained = containmentFull.get(territory); 174 if (contained == null) { 175 return; 176 } 177 for (String subitem : contained) { 178 if (!toOrder.containsKey(subitem)) { 179 toOrder.put(subitem, ++level); 180 } 181 } 182 for (String subitem : contained) { 183 initOrder(subitem); 184 } 185 } 186 resetOrder(String newTerritory, String oldTerritory)187 private static void resetOrder(String newTerritory, String oldTerritory) { 188 // final Integer newOrder = toOrder.get(newTerritory); 189 // if (newOrder != null) { 190 // throw new IllegalArgumentException(newTerritory + " already defined as " + newOrder); 191 // } 192 final Integer oldOrder = toOrder.get(oldTerritory); 193 if (oldOrder == null) { 194 hadErrors = true; 195 throw new IllegalArgumentException(oldTerritory + " not yet defined"); 196 } 197 toOrder.put(newTerritory, oldOrder); 198 } 199 getContinents()200 public Set<String> getContinents() { 201 return continents; 202 } 203 getSubontinents()204 public Set<String> getSubontinents() { 205 return subcontinents; 206 } 207 getAllDirected(Multimap<String, String> multimap, String lang)208 public static Set<List<String>> getAllDirected(Multimap<String, String> multimap, String lang) { 209 LinkedHashSet<List<String>> result = new LinkedHashSet<>(); 210 getAllDirected( 211 multimap, 212 lang, 213 new ArrayList<String>(), 214 result, 215 new LinkedList<String>(), 216 new HashSet<String>()); 217 return result; 218 } 219 getAllDirected( Multimap<String, String> multimap, String lang, ArrayList<String> target, Set<List<String>> targets, List<String> depth, Set<String> loops)220 private static void getAllDirected( 221 Multimap<String, String> multimap, 222 String lang, 223 ArrayList<String> target, 224 Set<List<String>> targets, 225 List<String> depth, 226 Set<String> loops) { 227 int alreadySawThis = depth.indexOf(lang); 228 if (alreadySawThis != -1) { 229 hadErrors = true; 230 if (loops.add(lang)) { 231 System.err.println( 232 "WARNING: CLDR-15020 getAllDirected(): Loops Detected: " 233 + String.join(" -> ", depth) 234 + " -> " 235 + lang); 236 } // else: already reported it. 237 return; 238 } else { 239 depth.add(lang); 240 } 241 if (!target.add(lang)) { 242 hadErrors = true; 243 throw new StackOverflowError("Error: Saw " + lang + " multiple times in this target."); 244 } else if (depth.size() > 999) { 245 hadErrors = true; 246 throw new StackOverflowError("Error: Too deep getting " + lang); 247 } 248 Collection<String> parents = multimap.get(lang); 249 int size = parents.size(); 250 if (size == 0) { 251 targets.add(target); 252 } else if (size == 1) { 253 for (String parent : parents) { 254 if (parent.equals(lang)) { 255 hadErrors = true; 256 System.err.println("ERR: " + lang + " is its own parent"); 257 } else { 258 getAllDirected(multimap, parent, target, targets, depth, loops); 259 } 260 } 261 } else { 262 for (String parent : parents) { 263 if (parent.equals(lang)) { 264 hadErrors = true; 265 System.err.println("ERR: " + lang + " is its own parent"); 266 } else { 267 getAllDirected( 268 multimap, 269 parent, 270 (ArrayList<String>) target.clone(), 271 targets, 272 depth, 273 loops); 274 } 275 } 276 } 277 depth.remove(lang); // pop stack 278 } 279 280 /** 281 * For each leaf region (eg "CO"), return all containers [019, 419, 005, 001] 282 * 283 * @param leaf 284 * @return 285 */ leafToContainer(String leaf)286 public static Set<String> leafToContainer(String leaf) { 287 return leavesToContainers.get(leaf); 288 } 289 isLeaf(String region)290 public static boolean isLeaf(String region) { 291 return leavesToContainers.containsKey(region); 292 } 293 } 294