1 package org.unicode.cldr.tool;
2 
3 import java.io.IOException;
4 import java.util.Collection;
5 import java.util.Map;
6 import java.util.Map.Entry;
7 import java.util.Set;
8 import java.util.TreeSet;
9 
10 import org.unicode.cldr.util.CldrUtility;
11 import org.unicode.cldr.util.Pair;
12 import org.unicode.cldr.util.Rational.FormatStyle;
13 import org.unicode.cldr.util.UnitConverter;
14 import org.unicode.cldr.util.UnitPreferences;
15 import org.unicode.cldr.util.UnitPreferences.UnitPreference;
16 
17 import com.google.common.base.Joiner;
18 import com.google.common.base.Splitter;
19 import com.google.common.collect.BiMap;
20 import com.google.common.collect.HashBiMap;
21 import com.google.common.collect.HashMultiset;
22 import com.google.common.collect.Multimap;
23 import com.google.common.collect.Multiset;
24 
25 public class ChartUnitPreferences extends Chart {
26 
main(String[] args)27     public static void main(String[] args) {
28         new ChartUnitPreferences().writeChart(null);
29     }
30 
31     @Override
getDirectory()32     public String getDirectory() {
33         return FormattedFileWriter.CHART_TARGET_DIR;
34     }
35 
36     @Override
getTitle()37     public String getTitle() {
38         return "Unit Preferences";
39     }
40 
41     @Override
getExplanation()42     public String getExplanation() {
43         return "<p>Unit Preferences provide a way to get the units that are appropriate for a region, and usage, and threshold amounts. "
44             + "The <a href='unit_conversions.html' target='unit_conversions'>Unit Conversions</a> are used to handle conversion of units needed to use the preferences."
45             + "This release adds additional structure for usage and threshold amount, allowing for more additions of regions, usages, thresholds, and units in future releases.</p>"
46             + "<ul>"
47             + "<li>The Base Unit shows the unit whose amount is to be compared to the ‘If ≥’ amount of the Result Unit."
48             + "<li>The unit identifiers are internal, and would 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."
49             + "<li>The Usage shows the type of usage; <i>default</i> is used as a fallback no more specific match is found."
50             + "<li>The Sample region  represents a set of regions if it has a superscript. See the table at the bottom. 001 (World) is the default if no more specific region is found.</li>"
51             + "<li>The ‘If ≥’ column shows the thresholds: the first line for a given region where the input amount is greater or equal applies. "
52             + "For example, for 0.5km as input for [area, default, 001] would result in <i>hectare</i>.</li>"
53             + "<li>" + ChartUnitConversions.RATIONAL_MSG + "</li>\n"
54             + "<li>"  + ChartUnitConversions.QUANTITY_MSG + "</li>"
55             + "<li>" + ChartUnitConversions.SPEC_GENERAL_MSG + ", and how to fall back if a given usage or region is not found.</li>\n"
56             + "</ul>"
57             + dataScrapeMessage("/tr35-general.html#Contents", "common/testData/units/unitPreferencesTest.txt", "common/supplemental/units.xml");
58     }
59 
60     @Override
writeContents(FormattedFileWriter pw)61     public void writeContents(FormattedFileWriter pw) throws IOException {
62         // #   Quantity;   Usage;  Region; Input (r);  Input (d);  Input Unit; Output (r); Output (d); Output Unit
63 
64         TablePrinter tablePrinter = new TablePrinter()
65             .addColumn("Base Unit", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true)
66             .setBreakSpans(true)
67             .setRepeatHeader(true)
68             .addColumn("Usage", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true)
69             .addColumn("Sample Region", "class='source'", null, "class='source'", true)
70             .addColumn("If ≥", "class='source'", null, "class='source'", true)
71             .setCellAttributes("class='target' style='text-align:right'")
72             .addColumn("Result Unit", "class='target'", null, "class='target'", true)
73             .addColumn("Skeleton", "class='target'", null, "class='target'", true)
74             .addColumn("Quantity", "class='target'", null, "class='target'", true);
75 
76         UnitConverter converter = SDI.getUnitConverter();
77         UnitPreferences prefs = SDI.getUnitPreferences();
78         Samples samples = new Samples();
79 
80         for (Entry<String, Map<String, Multimap<Set<String>, UnitPreference>>> entry : prefs.getData().entrySet()) {
81             String quantity = entry.getKey();
82             String baseUnit = converter.getBaseUnitFromQuantity(quantity);
83             for (Entry<String, Multimap<Set<String>, UnitPreference>> entry2 : entry.getValue().entrySet()) {
84                 String usage = entry2.getKey();
85                 for (Entry<Set<String>, Collection<UnitPreference>> entry3 : entry2.getValue().asMap().entrySet()) {
86 
87                     Set<String> regions = entry3.getKey();
88                     Pair<String, Integer> sampleRegion = samples.getSample(regions);
89                     final String sampleRegionStr = sampleDisplay(sampleRegion);
90 
91                     for (UnitPreference pref : entry3.getValue()) {
92                         tablePrinter.addRow()
93                         .addCell(baseUnit)
94                         .addCell(usage)
95                         .addCell(sampleRegionStr)
96                         .addCell(pref.geq.toString(FormatStyle.html))
97                         .addCell(Joiner.on(" & ").join(Splitter.on("-and-").split(pref.unit)))
98                         .addCell(pref.skeleton)
99                         .addCell(quantity)
100                         .finishRow();
101 
102                     }
103                 }
104             }
105         }
106         pw.write(tablePrinter.toTable());
107         pw.write("<br><h1>Region Sets</h1>\n");
108         TablePrinter tablePrinter2 = new TablePrinter()
109             .addColumn("Sample Region", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true)
110             .addColumn("Region Set", "class='target'", CldrUtility.getDoubleLinkMsg(), "class='source'", true);
111 
112         TreeSet<Pair<String, Integer>> sorted = new TreeSet<>(samples.setToSample.values());
113         for (Pair<String, Integer> pair : sorted) {
114             tablePrinter2.addRow()
115             .addCell(sampleDisplay(pair))
116             .addCell(Joiner.on(", ").join(samples.setToSample.inverse().get(pair)))
117             .finishRow();
118         }
119         pw.write(tablePrinter2.toTable());
120     }
121 
sampleDisplay(Pair<String, Integer> sampleRegion)122     private String sampleDisplay(Pair<String, Integer> sampleRegion) {
123         return sampleRegion.getFirst() + (sampleRegion.getSecond() < 0 ?  "" : "<sup>" + sampleRegion.getSecond() + "</sup>");
124     }
125 
126     private static final class Samples {
127         BiMap<Set<String>, Pair<String, Integer>> setToSample = HashBiMap.create();
128         Multiset<String> counters = HashMultiset.create();
129 
getSample(Set<String> regions)130         private Pair<String, Integer> getSample(Set<String> regions) {
131             if (regions.size() == 1) {
132                 return new Pair<>(regions.iterator().next(), -1);
133             }
134             Pair<String, Integer> sample = setToSample.get(regions);
135             if (sample == null) {
136                 String sampleBase = regions.iterator().next() + ", …";
137                 counters.add(sampleBase);
138                 setToSample.put(regions, sample = new Pair<>(sampleBase, counters.count(sampleBase)));
139             }
140             return sample;
141         }
142     }
143 }
144