1 package org.unicode.cldr.util; 2 3 import java.util.Collection; 4 import java.util.LinkedHashMap; 5 import java.util.Map; 6 import java.util.Map.Entry; 7 import java.util.Objects; 8 import java.util.Set; 9 import java.util.TreeMap; 10 import java.util.TreeSet; 11 12 import com.google.common.base.Joiner; 13 import com.google.common.base.Splitter; 14 import com.google.common.collect.ImmutableSet; 15 import com.google.common.collect.LinkedHashMultimap; 16 import com.google.common.collect.Multimap; 17 import com.ibm.icu.impl.locale.XCldrStub.ImmutableMap; 18 import com.ibm.icu.util.Freezable; 19 20 public class UnitPreferences implements Freezable<UnitPreferences> { 21 Map<String, Map<String, Multimap<Set<String>, UnitPreference>>> quantityToUsageToRegionsToInfo = new TreeMap<>(); 22 Set<String> usages = new TreeSet<>(); 23 24 /** 25 * Special class encapsulating 26 * @author markdavis 27 * 28 */ 29 public static final class UnitPreference implements Comparable<UnitPreference>{ 30 public final Rational geq; 31 public final String unit; 32 public final String skeleton; 33 UnitPreference(Rational geq, String unit, String skeleton)34 public UnitPreference(Rational geq, String unit, String skeleton) { 35 this.geq = geq; 36 this.unit = unit; 37 this.skeleton = skeleton == null ? "" : skeleton; 38 } 39 40 @Override compareTo(UnitPreference o)41 public int compareTo(UnitPreference o) { 42 int diff = geq.compareTo(o.geq); 43 if (diff != 0) { 44 return diff; 45 } 46 return unit.compareTo(o.unit); 47 } 48 @Override equals(Object obj)49 public boolean equals(Object obj) { 50 return compareTo((UnitPreference)obj) == 0; 51 } 52 @Override hashCode()53 public int hashCode() { 54 return Objects.hash(geq, unit); 55 } toString(String baseUnit)56 public String toString(String baseUnit) { 57 return geq + (baseUnit == null ? "": " " + baseUnit) + ", " + unit + (skeleton.isEmpty() ? "" : ", " + skeleton); 58 } 59 @Override toString()60 public String toString() { 61 return toString(null); 62 } 63 } 64 65 static private final Splitter SPLIT_SPACE = Splitter.on(' ').trimResults().omitEmptyStrings(); 66 static public Splitter SPLIT_AND = Splitter.on("-and-"); 67 add(String quantity, String usage, String regions, String geq, String skeleton, String unit)68 public void add(String quantity, String usage, String regions, String geq, String skeleton, String unit) { 69 usages.add(usage); 70 Map<String, Multimap<Set<String>, UnitPreference>> usageToRegionsToInfo = quantityToUsageToRegionsToInfo.get(quantity); 71 if (usageToRegionsToInfo == null) { 72 quantityToUsageToRegionsToInfo.put(quantity, usageToRegionsToInfo = new TreeMap<>()); 73 } 74 Multimap<Set<String>, UnitPreference> regionsToInfo = usageToRegionsToInfo.get(usage); 75 if (regionsToInfo == null) { 76 usageToRegionsToInfo.put(usage, regionsToInfo = LinkedHashMultimap.create()); 77 } 78 Rational newGeq = geq == null || geq.isEmpty() ? Rational.ONE : Rational.of(geq); 79 final UnitPreference newUnitPref = new UnitPreference(newGeq, unit, skeleton); 80 81 regionsToInfo.put(ImmutableSet.copyOf(new TreeSet<>(SPLIT_SPACE.splitToList(regions))), newUnitPref); 82 } 83 84 boolean frozen; 85 86 @Override isFrozen()87 public boolean isFrozen() { 88 return frozen; 89 } 90 91 @Override freeze()92 public UnitPreferences freeze() { 93 if (!frozen) { 94 frozen = true; 95 quantityToUsageToRegionsToInfo = CldrUtility.protectCollection(quantityToUsageToRegionsToInfo); 96 usages = ImmutableSet.copyOf(usages); 97 } 98 return this; 99 } 100 101 @Override cloneAsThawed()102 public UnitPreferences cloneAsThawed() { 103 throw new UnsupportedOperationException(); 104 } 105 106 /** 107 * quantity => usage => region => geq => [unit, skeleton] 108 * @return 109 */ getData()110 public Map<String, Map<String, Multimap<Set<String>, UnitPreference>>> getData() { 111 return quantityToUsageToRegionsToInfo; 112 } 113 114 static final Joiner JOIN_SPACE = Joiner.on(' '); 115 116 @Override toString()117 public String toString() { 118 StringBuilder buffer = new StringBuilder(); 119 int order = 0; 120 for (Entry<String, Map<String, Multimap<Set<String>, UnitPreference>>> entry1 : quantityToUsageToRegionsToInfo.entrySet()) { 121 String quantity = entry1.getKey(); 122 for (Entry<String, Multimap<Set<String>, UnitPreference>> entry2 : entry1.getValue().entrySet()) { 123 String usage = entry2.getKey(); 124 for (Entry<Set<String>, Collection<UnitPreference>> entry : entry2.getValue().asMap().entrySet()) { 125 Set<String> regions = entry.getKey(); 126 for (UnitPreference up : entry.getValue()) { 127 buffer.append("\n" + up.unit + "\t;\t" + getPath(order++, quantity, usage, regions, up.geq, up.skeleton)); 128 } 129 } 130 } 131 } 132 return buffer.toString(); 133 } 134 getPath(int order, String quantity, String usage, Collection<String> regions, Rational geq, String skeleton)135 public String getPath(int order, String quantity, String usage, Collection<String> regions, Rational geq, String skeleton) { 136 // <unitPreferences category="length" usage="person" scope="small"> 137 // <unitPreference regions="001">centimeter</unitPreference> 138 return "//supplementalData/unitPreferenceData/unitPreferences" 139 + "[@category=\"" + quantity + "\"]" 140 + "[@usage=\"" + usage + "\"]" 141 + "/unitPreference" 142 + "[@_q=\"" + order + "\"]" 143 + "[@regions=\"" + JOIN_SPACE.join(regions) + "\"]" 144 + (geq == Rational.ONE ? "" : "[@geq=\"" + geq + "\"]") 145 + (skeleton.isEmpty() ? "" : "[@skeleton=\"" + skeleton + "\"]") 146 ; 147 } 148 149 /** 150 * Returns the data converted to single regions, and using base units 151 * @return 152 */ getFastMap(UnitConverter converter)153 public Map<String, Map<String, Map<String, UnitPreference>>> getFastMap(UnitConverter converter) { 154 Map<String, Map<String, Map<String, UnitPreference>>> result = new LinkedHashMap<>(); 155 for (Entry<String, Map<String, Multimap<Set<String>, UnitPreference>>> entry1 : quantityToUsageToRegionsToInfo.entrySet()) { 156 String quantity = entry1.getKey(); 157 Map<String, Map<String, UnitPreference>> result2 = new LinkedHashMap<>(); 158 result.put(quantity, result2); 159 160 for (Entry<String, Multimap<Set<String>, UnitPreference>> entry2 : entry1.getValue().entrySet()) { 161 String usage = entry2.getKey(); 162 Map<String, UnitPreference> result3 = new LinkedHashMap<>(); 163 result2.put(usage, result3); 164 for (Entry<Set<String>, Collection<UnitPreference>> entry : entry2.getValue().asMap().entrySet()) { 165 Set<String> regions = entry.getKey(); 166 for (UnitPreference up : entry.getValue()) { 167 String unit = SPLIT_AND.split(up.unit).iterator().next(); // first unit 168 quantity = converter.getQuantityFromUnit(unit, false); 169 String baseUnit = converter.getBaseUnitFromQuantity(quantity); 170 Rational geq = converter.parseRational(String.valueOf(up.geq)); 171 Rational value = converter.convert(geq, unit, baseUnit, false); 172 if (value.equals(Rational.NaN)) { 173 converter.convert(geq, unit, baseUnit, true); // debug 174 } 175 UnitPreference up2 = new UnitPreference(value, up.unit, up.skeleton); 176 for (String region : regions) { 177 result3.put(region, up2); 178 } 179 } 180 } 181 } 182 } 183 return ImmutableMap.copyOf(result); 184 } 185 getUsages()186 public Set<String> getUsages() { 187 return usages; 188 } 189 } 190