1 package org.unicode.cldr.unittest; 2 3 import com.google.common.base.Joiner; 4 import com.google.common.collect.ImmutableMap; 5 import com.google.common.collect.ImmutableSet; 6 import com.google.common.collect.Multimap; 7 import com.google.common.collect.TreeMultimap; 8 import com.ibm.icu.util.Output; 9 import java.util.Arrays; 10 import java.util.Collection; 11 import java.util.List; 12 import java.util.Map.Entry; 13 import java.util.Set; 14 import java.util.TreeSet; 15 import java.util.regex.Matcher; 16 import java.util.regex.Pattern; 17 import org.unicode.cldr.util.CLDRConfig; 18 import org.unicode.cldr.util.Pair; 19 import org.unicode.cldr.util.Rational; 20 import org.unicode.cldr.util.Rational.FormatStyle; 21 import org.unicode.cldr.util.SupplementalDataInfo; 22 import org.unicode.cldr.util.SupplementalDataInfo.UnitIdComponentType; 23 import org.unicode.cldr.util.UnitConverter; 24 import org.unicode.cldr.util.UnitConverter.ConversionInfo; 25 import org.unicode.cldr.util.UnitConverter.TargetInfo; 26 import org.unicode.cldr.util.UnitConverter.UnitId; 27 import org.unicode.cldr.util.UnitConverter.UnitSystem; 28 import org.unicode.cldr.util.UnitParser; 29 30 public class GenerateNewUnits { 31 32 private static final CLDRConfig CLDR_CONFIG = CLDRConfig.getInstance(); 33 private static final CLDRConfig info = CLDR_CONFIG; 34 private static final SupplementalDataInfo SDI = info.getSupplementalDataInfo(); 35 private static final UnitConverter converter = SDI.getUnitConverter(); 36 37 private static final String CHECK_UNIT = "kilogram-force"; 38 main(String[] args)39 public static void main(String[] args) { 40 Set<String> argSet = Set.copyOf(Arrays.asList(args)); 41 boolean SI_ACCEPTED = argSet.isEmpty() || argSet.contains("accepted"); 42 boolean SYMBOLS = argSet.isEmpty() || argSet.contains("symbols"); 43 boolean GENERATE_XML = argSet.isEmpty() || argSet.contains("xml"); 44 boolean OTHER = argSet.isEmpty() || argSet.contains("plain"); 45 46 boolean TEST_PARSER = argSet.contains("parser"); 47 Output<String> base = new Output<>(); 48 49 int count = 0; 50 Set<ExternalUnitConversionData> cleanedNist = clean(NistUnits.externalConversionData); 51 UnitParser up = new UnitParser(); 52 String lastQuantity = ""; 53 54 if (SI_ACCEPTED) { 55 System.out.println("\n# SI_Accepted\n"); 56 for (ExternalUnitConversionData data : NistUnits.externalConversionData) { 57 if (data.systems.contains(UnitSystem.si_acceptable)) { 58 ConversionInfo convert = converter.parseUnitId(data.source, base, false); 59 System.out.println((convert == null ? "OUT" : "IN") + "\t" + data); 60 } 61 } 62 } 63 if (SYMBOLS) { 64 System.out.println("# Symbols\n"); 65 66 Multimap<String, String> unitsToSymbols = TreeMultimap.create(); 67 Multimap<String, String> symbolsToUnits = TreeMultimap.create(); 68 Matcher simple = Pattern.compile("^[^ ·/0-9]*$").matcher(""); 69 for (ExternalUnitConversionData data : NistUnits.externalConversionData) { 70 if (data.symbol != null) { 71 ConversionInfo convert = converter.parseUnitId(data.source, base, false); 72 if (convert != null && simple.reset(data.symbol).matches()) { 73 unitsToSymbols.put(data.source, data.symbol); 74 symbolsToUnits.put(data.symbol, data.source); 75 } 76 } 77 } 78 for (Entry<String, Collection<String>> entry : unitsToSymbols.asMap().entrySet()) { 79 final String sourceUnit = entry.getKey(); 80 System.out.println( 81 converter.getQuantityFromUnit(sourceUnit, false) // 82 + "\t" 83 + sourceUnit // 84 + "\t" 85 + Joiner.on('\t').join(entry.getValue()) 86 + "\t" 87 + converter.getSystems(sourceUnit)); 88 } 89 for (Entry<String, Collection<String>> entry : symbolsToUnits.asMap().entrySet()) { 90 if (entry.getValue().size() > 1) { 91 System.out.println( 92 "Ambiguous! " 93 + entry.getKey() 94 + "\t" 95 + Joiner.on('\t').join(entry.getValue())); 96 } 97 } 98 } 99 if (GENERATE_XML) { 100 System.out.println("\n# XML missing\n"); 101 for (ExternalUnitConversionData data : cleanedNist) { 102 103 if (!lastQuantity.equals(data.quantity)) { 104 System.out.println("<!-- " + data.quantity + "-->"); 105 lastQuantity = data.quantity; 106 } 107 System.out.println( 108 "\t\t<convertUnit" 109 + " source='" 110 + data.source 111 + "'" 112 + (data.symbol == null ? "" : " symbol='" + data.source + "'") 113 + " baseUnit='" 114 + data.target 115 + "'" 116 + " factor='" 117 + data.info.factor 118 + "'" 119 + " systems=\'???\'" 120 + "/>"); 121 } 122 if (OTHER) { 123 System.out.println("\n# Missing\n"); 124 for (ExternalUnitConversionData data : cleanedNist) { 125 System.out.println( 126 ++count 127 + "\t" 128 + data.quantity 129 + "\t" 130 + data.source 131 + "\t" 132 + data.symbol 133 + "\t⟹\t" 134 + data.info.factor 135 + " × " 136 + data.target 137 + "\t" 138 + data.info.factor.toString(FormatStyle.approx)); 139 } 140 } 141 if (TEST_PARSER) { 142 System.out.println("\n# Check parser\n"); 143 for (ExternalUnitConversionData data : cleanedNist) { 144 up.set(data.source); 145 try { 146 List<Pair<UnitIdComponentType, String>> list = up.getRemaining(); 147 System.out.println(data.source + "\t" + list.size() + "\t⟹\t" + list); 148 } catch (Exception e1) { 149 System.out.println(e1.getMessage() + ", " + data); 150 e1.printStackTrace(); 151 } 152 } 153 } 154 } 155 } 156 157 /** Filter out the most useful conversions */ clean( Set<ExternalUnitConversionData> externalconversiondata)158 private static Set<ExternalUnitConversionData> clean( 159 Set<ExternalUnitConversionData> externalconversiondata) { 160 Multimap<String, ExternalUnitConversionData> cleaned = TreeMultimap.create(); 161 Output<String> base = new Output<>(); 162 for (ExternalUnitConversionData data : NistUnits.externalConversionData) { 163 if (data.source.equals(CHECK_UNIT)) { 164 SDI.getUnitIdComponentType(CHECK_UNIT); 165 } 166 167 // skip the ones we have already 168 169 ConversionInfo convert = converter.parseUnitId(data.source, base, false); 170 if (convert == null) { 171 cleaned.put(data.source, data); 172 } 173 } 174 Set<ExternalUnitConversionData> result = new TreeSet<>(); 175 ImmutableSet<String> absoluteTemperature = 176 ImmutableSet.of("celsius", "fahrenheit", "kelvin", "rankine"); 177 Output<String> baseUnit = new Output<>(); 178 for (Entry<String, Collection<ExternalUnitConversionData>> items : 179 cleaned.asMap().entrySet()) { 180 final String source = items.getKey(); 181 if (absoluteTemperature.contains(source)) { 182 continue; 183 } 184 185 final Collection<ExternalUnitConversionData> mappings = items.getValue(); 186 if (source.equals(CHECK_UNIT)) { 187 int debug = 0; 188 } 189 ExternalUnitConversionData revisedItem = findBestMapping(baseUnit, source, mappings); 190 result.add(revisedItem); 191 } 192 return result; 193 } 194 195 /** 196 * Get the best mapping 197 * 198 * @param baseUnit 199 * @param source 200 * @param mappings 201 * @return 202 */ findBestMapping( Output<String> baseUnit, final String source, final Collection<ExternalUnitConversionData> mappings)203 public static ExternalUnitConversionData findBestMapping( 204 Output<String> baseUnit, 205 final String source, 206 final Collection<ExternalUnitConversionData> mappings) { 207 String bestTarget = null; 208 Rational bestFactor = null; 209 String bestQuantity = null; 210 String bestSymbol = null; 211 for (ExternalUnitConversionData data : mappings) { 212 ConversionInfo conversionInfo = converter.parseUnitId(data.target, baseUnit, false); 213 if (conversionInfo != null) { 214 String target = baseUnit.value; 215 Rational endFactor = conversionInfo.convert(data.info.factor); 216 if (bestTarget == null) { 217 bestTarget = target; 218 bestFactor = endFactor; 219 bestQuantity = data.quantity; 220 bestSymbol = data.symbol; 221 } 222 } else { 223 TargetInfo baseUnit2 = NistUnits.derivedUnitToConversion.get(data.target); 224 if (baseUnit2 != null) { 225 bestTarget = baseUnit2.target; 226 bestFactor = baseUnit2.unitInfo.factor.multiply(data.info.factor); 227 bestQuantity = data.quantity; 228 bestSymbol = data.symbol; 229 } 230 } 231 } 232 UnitId targetId = converter.createUnitId(bestTarget); 233 bestTarget = targetId == null ? bestTarget : targetId.resolve().toString(); 234 String quantity = converter.getQuantityFromUnit(bestTarget, false); 235 bestQuantity = quantity == null ? "•" + bestQuantity : quantity; 236 return new ExternalUnitConversionData( 237 bestQuantity, 238 source, 239 bestSymbol, 240 bestTarget, 241 bestFactor, 242 Rational.ZERO, 243 null, 244 null, 245 null); 246 } 247 248 static final ImmutableMap<String, String> quantityFromUnit = 249 ImmutableMap.<String, String>builder() 250 .put("curie", "radioactivity") 251 .put("kayser", "spectroscopy") 252 .put("gon", "angle") 253 .put( 254 "cubic-second-square-ampere-per-kilogram-square-meter", 255 "electrical-conductance") 256 .put("gram-per-meter", "dup") 257 .put("joule-per-kelvin", "entropy") 258 .put("joule-per-kilogram-kelvin", "specific-heat") 259 .put("joule-per-square-meter", "radiant-exposure") 260 .put("kelvin-per-watt", "absolute-thermal-resistance") 261 .put("kilogram-per-meter", "linear-density") 262 .put("kilogram-per-second", "mass-flow-rate") 263 .put("kilogram-per-square-meter", "surface-density") 264 .put("kilogram-per-square-second-ampere", "magnetic-flux-density") 265 .put("kilogram-square-meter-per-square-second-ampere", "magnetic-flux") 266 .put("kilogram-square-meter-per-square-second-square-ampere", "inductance") 267 .put("meter-kelvin-per-watt", "thermal-resistance-coefficient") 268 .put("meter-per-meter", "angle") 269 .put("mole-per-second", "enzymatic-activity") 270 .put("ohm-meter", "electric-resistivity") 271 .put("ohm-square-millimeter-per-meter", "electrical-resistivity") 272 .put("pascal-second", "dynamic-viscosity") 273 .put("per-pascal-second", "dup") 274 .put("per-second", "radioactivity") 275 .put("pow4-second-square-ampere-per-kilogram-square-meter", "capacitance") 276 .put("second-ampere", "emu-of-charge") 277 .put("square-meter-per-second", "kinematic-viscosity") 278 .put("square-meter-per-square-meter", "solid-angle") 279 .put("square-meter-per-square-second", "dose") 280 .put("watt-per-square-meter", "irradiance") 281 .put("watt-per-square-meter-kelvin", "thermal-heat-transfer-coefficient") 282 .put("watt-per-meter-kelvin", "thermal-conductivity") 283 .put("square-meter-kelvin-per-watt", "thermal-insulance") 284 .put( 285 "kilogram-square-meter-per-meter-cubic-second-kelvin", 286 "thermal-conductivity") 287 .put("kilogram-second-per-meter-square-second", "viscosity") 288 .put("kilogram-square-meter-per-square-meter-cubic-second", "surface-tension") 289 // kilogram-square-meter-per-cubic-second 290 .build(); 291 } 292