1 package org.unicode.cldr.unittest; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.util.ArrayList; 6 import java.util.Collections; 7 import java.util.LinkedHashSet; 8 import java.util.List; 9 import java.util.Map; 10 import java.util.Set; 11 import java.util.TreeMap; 12 import java.util.regex.Matcher; 13 import java.util.regex.Pattern; 14 import java.util.stream.Collectors; 15 import java.util.stream.Stream; 16 17 import org.unicode.cldr.util.CldrUtility; 18 import org.unicode.cldr.util.Rational; 19 import org.unicode.cldr.util.UnitConverter.ConversionInfo; 20 import org.unicode.cldr.util.UnitConverter.TargetInfo; 21 22 import com.google.common.base.Joiner; 23 import com.google.common.base.Splitter; 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.ImmutableMap; 26 import com.google.common.collect.ImmutableMultimap; 27 import com.google.common.collect.ImmutableSet; 28 import com.google.common.collect.LinkedHashMultimap; 29 import com.google.common.collect.Multimap; 30 import com.google.common.collect.TreeMultimap; 31 import com.ibm.icu.util.ICUUncheckedIOException; 32 33 final class NistUnits { 34 final static Multimap<String,String> unitToQuantity; 35 final static Map<String, TargetInfo> derivedUnitToConversion; 36 final static List<ExternalUnitConversionData> externalConversionData; 37 final static Multimap<String, String> idChanges; 38 final static Set<String> skipping; 39 40 static final Splitter SPLIT_MIDDOT = Splitter.on('·').trimResults(); 41 static final Pattern flatExponent = Pattern.compile("([a-zA-Z]+)(-?[0-9]+)?"); 42 static final Splitter SPLIT_TABS = Splitter.on('\t').trimResults(); 43 static final Splitter SPLIT_COMMAS = Splitter.on(',').trimResults(); 44 static final Splitter SPLIT_PARENS = Splitter.on('(').trimResults(); 45 46 47 static { 48 try { 49 Multimap<String, String> _idChanges = LinkedHashMultimap.create(); 50 Set<String> _skipping = new LinkedHashSet<>(); 51 52 List<ExternalUnitConversionData> _externalConversionData = new ArrayList<>(); try(BufferedReader in = CldrUtility.getUTF8Data("external/nistConversions.txt"))53 try (BufferedReader in = CldrUtility.getUTF8Data("external/nistConversions.txt")) { 54 String quantity = null; 55 try (Stream<String> s = in.lines()) { 56 for (String line : (Iterable<String>) s::iterator) { 57 if (line.startsWith("#") 58 || line.equals("To convert from\tto\tMultiply by") 59 || line.startsWith("degree Fahrenheit hour square foot per British thermal unitth inch") // bad NIST data 60 ) { 61 continue; 62 } 63 List<String> parts = SPLIT_TABS.splitToList(line); 64 switch(parts.size()) { 65 case 1: 66 quantity = parts.get(0); 67 break; 68 case 4: 69 Rational factor = Rational.of((parts.get(2) + parts.get(3)).replace(" ", "")); 70 ExternalUnitConversionData data = new ExternalUnitConversionData(quantity, parts.get(0), parts.get(1), factor, line, _idChanges); 71 _externalConversionData.add(data); 72 break; 73 default: 74 _skipping.add(line); 75 } 76 } 77 } 78 } 79 80 Map<String, TargetInfo> unitToTargetInfo = new TreeMap<>(); 81 Map<String,String> _symbolToUnit = new TreeMap<>(); 82 Multimap<String,String> _unitToQuantity = TreeMultimap.create(); try(BufferedReader in = CldrUtility.getUTF8Data("external/nistBaseUnits.txt"))83 try (BufferedReader in = CldrUtility.getUTF8Data("external/nistBaseUnits.txt")) { 84 try (Stream<String> s = in.lines()) { 85 for (String line : (Iterable<String>) s::iterator) { 86 if (line.startsWith("#")) { 87 continue; 88 } 89 List<String> parts = SPLIT_TABS.splitToList(line); 90 //#Base quantity Name Symbol 91 String quantity2 = parts.get(0); 92 String name = parts.get(1); 93 String symbol = parts.get(2); 94 switch(parts.size()) { 95 case 3: 96 _symbolToUnit.put(symbol, name); 97 _unitToQuantity.put(name, quantity2); 98 break; 99 } 100 } 101 } 102 } 103 try(BufferedReader in = CldrUtility.getUTF8Data("external/nistDerivedUnits.txt"))104 try (BufferedReader in = CldrUtility.getUTF8Data("external/nistDerivedUnits.txt")) { 105 try (Stream<String> s = in.lines()) { 106 for (String line : (Iterable<String>) s::iterator) { 107 if (line.startsWith("#")) { 108 continue; 109 } 110 List<String> parts = SPLIT_TABS.splitToList(line); 111 // #Quantity Special Name Special symbol Expression in terms of other SI units Expression in terms of SI base units 112 113 String quantity = parts.get(0); 114 List<String> quantities = SPLIT_COMMAS.splitToList(quantity).stream() 115 .map(x -> SPLIT_PARENS.split(parts.get(0)).iterator().next()) 116 .collect(Collectors.toList()); 117 quantity = Joiner.on(", ").join(quantities); 118 119 String name = SPLIT_PARENS.split(parts.get(1)).iterator().next(); 120 if (name.equals("degree Celsius")) { 121 name = "celsius"; 122 } 123 124 String symbol = parts.get(2); 125 String expressionInOtherSymbols = parts.get(4); 126 String expressionInBaseSymbols = parts.get(4); 127 _symbolToUnit.put(symbol, name); 128 _unitToQuantity.putAll(name, quantities); 129 130 final String targetUnit = getUnitFromSymbols(expressionInBaseSymbols, _symbolToUnit); 131 unitToTargetInfo.put(name, new TargetInfo(targetUnit, new ConversionInfo(Rational.ONE, Rational.ZERO), Collections.emptyMap())); 132 133 ExternalUnitConversionData data = new ExternalUnitConversionData(quantity, name, targetUnit, Rational.ONE, line, _idChanges); 134 _externalConversionData.add(data); 135 136 } 137 } 138 } 139 140 // Protect everything 141 142 skipping = ImmutableSet.copyOf(_skipping); 143 idChanges = ImmutableMultimap.copyOf(_idChanges); 144 externalConversionData = ImmutableList.copyOf(_externalConversionData); 145 unitToQuantity = ImmutableMultimap.copyOf(_unitToQuantity); 146 derivedUnitToConversion = ImmutableMap.copyOf(unitToTargetInfo); 147 } catch (IOException e) { 148 throw new ICUUncheckedIOException(e); 149 } 150 } 151 getUnitFromSymbols(String expressionInBaseSymbols, Map<String, String> symbolToUnit)152 public static String getUnitFromSymbols(String expressionInBaseSymbols, Map<String, String> symbolToUnit) { 153 // handle the irregualar formats 154 if (expressionInBaseSymbols.equals("m/m")) { 155 return "meter-per-meter"; 156 } else if (expressionInBaseSymbols.equals("m2/m2")) { 157 return "square-meter-per-square-meter"; 158 } 159 // m2 · kg · s-3 · A-1 160 StringBuilder numerator = new StringBuilder(); 161 StringBuilder denominator = new StringBuilder(); 162 for (String part : SPLIT_MIDDOT.split(expressionInBaseSymbols)) { 163 final Matcher parts = flatExponent.matcher(part); 164 if (!parts.matches()) { 165 throw new IllegalArgumentException("bad symbol: " + part); 166 } 167 String unit = symbolToUnit.get(parts.group(1)); 168 String pow = null; 169 int power = 0; 170 final String exponent = parts.group(2); 171 if (exponent != null) { 172 power = Integer.parseInt(exponent); 173 switch(Math.abs(power)) { 174 case 0: case 1: break;// skip 175 case 2: pow = "square-"; break; 176 case 3: pow = "cubic-"; break; 177 default: pow = "pow" + Math.abs(power) + "-"; break; 178 } 179 } 180 StringBuilder target = power >= 0 ? numerator : denominator; 181 if (target.length() != 0) { 182 target.append('-'); 183 } 184 if (pow != null) { 185 target.append(pow); 186 } 187 target.append(unit); 188 } 189 return (numerator.length() == 0 ? "1" : numerator) + (denominator.length() == 0 ? "" : "-per-" + denominator); 190 } 191 192 }