1 package org.unicode.cldr.tool; 2 3 import com.google.common.base.Joiner; 4 import com.ibm.icu.impl.Row; 5 import com.ibm.icu.impl.Row.R4; 6 import java.io.IOException; 7 import java.io.PrintWriter; 8 import java.util.EnumSet; 9 import java.util.Map.Entry; 10 import java.util.Set; 11 import java.util.TreeSet; 12 import org.unicode.cldr.util.CldrUtility; 13 import org.unicode.cldr.util.Rational; 14 import org.unicode.cldr.util.Rational.FormatStyle; 15 import org.unicode.cldr.util.UnitConverter; 16 import org.unicode.cldr.util.UnitConverter.TargetInfo; 17 import org.unicode.cldr.util.UnitConverter.UnitId; 18 import org.unicode.cldr.util.UnitConverter.UnitSystem; 19 20 public class ChartUnitConversions extends Chart { 21 22 public static final String QUANTITY_MSG = 23 "The units are grouped and ordered by Quantity (which are based on the NIST quantities, see " 24 + "<a href='https://www.nist.gov/pml/special-publication-811' target='nist811'>NIST 811</a>). Note that the quantities are informative."; 25 public static final String RATIONAL_MSG = 26 "Each numeric value is an exact rational. (Radians are an exception since the value of π is irrational; a rational approximation is used.)" 27 + "The format is a terminating decimal where possible; " 28 + "otherwise a repeating decimal if possible (where ˙ marks the start of the <a href='https://en.wikipedia.org/wiki/Repeating_decimal' target='wiki'>reptend</a>); " 29 + "otherwise a <a href='https://en.wikipedia.org/wiki/Rational_number' target='wiki'>rational number</a> (of the form <i>numerator/denominator</i>)." 30 + ""; 31 public static final String SPEC_GENERAL_MSG = 32 "The " 33 + ldmlSpecLink("/tr35-general.html#Contents") 34 + " should be consulted for more details, such as how to handle complex units (such as foot-per-minute) by converting the elements"; 35 main(String[] args)36 public static void main(String[] args) { 37 new ChartUnitConversions().writeChart(null); 38 } 39 40 @Override getDirectory()41 public String getDirectory() { 42 return FormattedFileWriter.CHART_TARGET_DIR; 43 } 44 45 @Override getTitle()46 public String getTitle() { 47 return "Unit Conversions"; 48 } 49 50 @Override getExplanation()51 public String getExplanation() { 52 return "<p>Unit Conversions provide conversions for units, such as meter ⟹ foot, " 53 + "so that a source units can be converted into what is needed for localized " 54 + "<a href='unit_preferences.html' target='unit_preferences'>Unit Preferences</a>. " 55 + "There are many possible units, and additional units and conversions will be added in future releases.</p>" 56 + "<ul>" 57 + "<li>Each Source Unit is converted to the Target Unit by multiplying it by the Factor and adding the Offset (if any).</li>" 58 + "<li>The unit identifiers are internal, and are to be localized for display to users. See <a href='https://www.unicode.org/cldr/charts/latest/by_type/units.area.html#hectare' target='units.area.hectare'>Hectare</a>, for example. " 59 + "<li>" 60 + RATIONAL_MSG 61 + "</li>" 62 + "<li>The Systems column indicates which systems the units are used in. For now, they just show the two ‘inch-pound’ systems.</li>" 63 + "<li>" 64 + QUANTITY_MSG 65 + "</li>" 66 + "<li>" 67 + SPEC_GENERAL_MSG 68 + ".</li>" 69 + "</ul>" 70 + dataScrapeMessage( 71 "/tr35-general.html#Contents", 72 "common/testData/units/unitsTest.txt", 73 "common/supplemental/units.xml"); 74 } 75 76 @Override writeContents(FormattedFileWriter pw)77 public void writeContents(FormattedFileWriter pw) throws IOException { 78 // <convertUnit source='ounce' baseUnit='kilogram' factor='lb_to_kg/16' systems="ussystem 79 // uksystem"/> 80 // <convertUnit source='fahrenheit' baseUnit='kelvin' factor='5/9' offset='2298.35/9' 81 // systems="ussystem uksystem"/> 82 83 TablePrinter tablePrinter = 84 new TablePrinter() 85 .addColumn("SortKey", "class='source'", null, "class='source'", true) 86 .setHidden(true) 87 .setSortPriority(0) 88 .addColumn( 89 "Quantity", 90 "class='source'", 91 CldrUtility.getDoubleLinkMsg(), 92 "class='source'", 93 true) 94 .setRepeatHeader(true) 95 .setBreakSpans(true) 96 .addColumn("Target", "class='source'", null, "class='source'", true) 97 .addColumn("Systems", "class='source'", null, "class='source'", true) 98 .addColumn( 99 "Source Unit", 100 "class='source'", 101 CldrUtility.getDoubleLinkMsg(), 102 "class='source'", 103 true) 104 .addColumn("Approx. Factor", "class='target'", null, "class='target'", true) 105 .setCellAttributes("class='target' style='text-align:right'") 106 .addColumn("Exact* Factor", "class='target'", null, "class='target'", true) 107 .setCellAttributes("class='target' style='text-align:right'") 108 .addColumn("Offset", "class='target'", null, "class='target'", true) 109 .setCellAttributes("class='target' style='text-align:right'"); 110 111 UnitConverter converter = SDI.getUnitConverter(); 112 converter.getSourceToSystems(); 113 114 Set<R4<UnitId, UnitSystem, Rational, String>> all = new TreeSet<>(); 115 116 for (Entry<String, TargetInfo> entry : converter.getInternalConversionData().entrySet()) { 117 String sourceUnit = entry.getKey(); 118 String quantity = converter.getQuantityFromUnit(sourceUnit, false); 119 TargetInfo targetInfo = entry.getValue(); 120 121 final EnumSet<UnitSystem> systems = 122 EnumSet.copyOf(converter.getSystemsEnum(sourceUnit)); 123 124 // to sort the right items together items together, put together a sort key 125 UnitSystem sortingSystem = systems.iterator().next(); 126 switch (sortingSystem) { 127 case si: 128 case si_acceptable: 129 sortingSystem = UnitSystem.metric; 130 break; 131 case uksystem: 132 sortingSystem = UnitSystem.ussystem; 133 break; 134 default: 135 } 136 UnitId targetUnitId = converter.createUnitId(targetInfo.target); 137 R4<UnitId, UnitSystem, Rational, String> sortKey = 138 Row.of(targetUnitId, sortingSystem, targetInfo.unitInfo.factor, sourceUnit); 139 all.add(sortKey); 140 141 // get some formatted strings 142 // TODO: handle specials here, CLDR-16329 additional PR or follow-on ticket 143 144 final String repeatingFactor = 145 targetInfo.unitInfo.factor.toString(FormatStyle.repeating); 146 final String basicFactor = targetInfo.unitInfo.factor.toString(FormatStyle.approx); 147 final String repeatingOffset = 148 targetInfo.unitInfo.offset.equals(Rational.ZERO) 149 ? "" 150 : targetInfo.unitInfo.offset.toString(FormatStyle.repeating); 151 152 String targetDisplay = targetInfo.target; 153 String normalized = converter.getStandardUnit(targetInfo.target); 154 if (!targetDisplay.equals(normalized)) { 155 targetDisplay = normalized + " = " + targetDisplay; 156 } 157 158 // now make a row 159 160 tablePrinter 161 .addRow() 162 .addCell(sortKey) 163 .addCell(quantity) 164 .addCell(targetDisplay) 165 .addCell(Joiner.on(", ").join(systems)) 166 .addCell(sourceUnit) 167 .addCell(basicFactor) 168 .addCell(repeatingFactor) 169 .addCell(repeatingOffset) 170 .finishRow(); 171 } 172 pw.write(tablePrinter.toTable()); 173 PrintWriter pw2 = new PrintWriter(System.out); 174 tablePrinter.toTsv(pw2); 175 pw2.flush(); 176 pw2.close(); 177 // for (R4<UnitId, UnitSystem, Rational, String> sk : all) { 178 // System.out.println(String.format("%s\t%s\t%s\t%s", sk.get0(), sk.get1(), 179 // sk.get2(), sk.get3())); 180 // } 181 } 182 } 183