|
9 | 9 | using CodeGen.Helpers;
|
10 | 10 | using CodeGen.JsonTypes;
|
11 | 11 | using Newtonsoft.Json;
|
12 |
| -using Newtonsoft.Json.Linq; |
13 | 12 | using Serilog;
|
14 | 13 | using QuantityGenerator = CodeGen.Generators.UnitsNetWrcGen.QuantityGenerator;
|
15 | 14 | using QuantityTypeGenerator = CodeGen.Generators.UnitsNetWrcGen.QuantityTypeGenerator;
|
@@ -60,11 +59,18 @@ public static void Generate(DirectoryInfo repositoryRoot)
|
60 | 59 |
|
61 | 60 | private static Quantity ParseQuantityFile(string jsonFile)
|
62 | 61 | {
|
63 |
| - var quantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(jsonFile, Encoding.UTF8), JsonSerializerSettings); |
64 |
| - AddPrefixUnits(quantity); |
65 |
| - FixConversionFunctionsForDecimalValueTypes(quantity); |
66 |
| - OrderUnitsByName(quantity); |
67 |
| - return quantity; |
| 62 | + try |
| 63 | + { |
| 64 | + var quantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(jsonFile, Encoding.UTF8), JsonSerializerSettings); |
| 65 | + AddPrefixUnits(quantity); |
| 66 | + FixConversionFunctionsForDecimalValueTypes(quantity); |
| 67 | + OrderUnitsByName(quantity); |
| 68 | + return quantity; |
| 69 | + } |
| 70 | + catch (Exception e) |
| 71 | + { |
| 72 | + throw new Exception($"Error parsing quantity JSON file: {jsonFile}", e); |
| 73 | + } |
68 | 74 | }
|
69 | 75 |
|
70 | 76 | private static void GenerateQuantity(StringBuilder sb, Quantity quantity, string filePath)
|
@@ -127,124 +133,61 @@ private static void AddPrefixUnits(Quantity quantity)
|
127 | 133 | foreach (Unit unit in quantity.Units)
|
128 | 134 | {
|
129 | 135 | // "Kilo", "Nano" etc.
|
130 |
| - for (var prefixIndex = 0; prefixIndex < unit.Prefixes.Length; prefixIndex++) |
| 136 | + foreach (Prefix prefix in unit.Prefixes) |
131 | 137 | {
|
132 |
| - Prefix prefix = unit.Prefixes[prefixIndex]; |
133 |
| - PrefixInfo prefixInfo = PrefixInfo.Entries[prefix]; |
134 |
| - |
135 |
| - unitsToAdd.Add(new Unit |
| 138 | + try |
136 | 139 | {
|
137 |
| - SingularName = $"{prefix}{unit.SingularName.ToCamelCase()}", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter" |
138 |
| - PluralName = $"{prefix}{unit.PluralName.ToCamelCase()}", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter" |
139 |
| - BaseUnits = null, // Can we determine this somehow? |
140 |
| - FromBaseToUnitFunc = $"({unit.FromBaseToUnitFunc}) / {prefixInfo.Factor}", |
141 |
| - FromUnitToBaseFunc = $"({unit.FromUnitToBaseFunc}) * {prefixInfo.Factor}", |
142 |
| - Localization = GetLocalizationForPrefixUnit(unit, prefixIndex, prefixInfo, quantity.Name), |
143 |
| - }); |
| 140 | + PrefixInfo prefixInfo = PrefixInfo.Entries[prefix]; |
| 141 | + |
| 142 | + unitsToAdd.Add(new Unit |
| 143 | + { |
| 144 | + SingularName = $"{prefix}{unit.SingularName.ToCamelCase()}", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter" |
| 145 | + PluralName = $"{prefix}{unit.PluralName.ToCamelCase()}", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter" |
| 146 | + BaseUnits = null, // Can we determine this somehow? |
| 147 | + FromBaseToUnitFunc = $"({unit.FromBaseToUnitFunc}) / {prefixInfo.Factor}", |
| 148 | + FromUnitToBaseFunc = $"({unit.FromUnitToBaseFunc}) * {prefixInfo.Factor}", |
| 149 | + Localization = GetLocalizationForPrefixUnit(unit.Localization, prefixInfo), |
| 150 | + }); |
| 151 | + } |
| 152 | + catch (Exception e) |
| 153 | + { |
| 154 | + throw new Exception($"Error parsing prefix {prefix} for unit {quantity.Name}.{unit.SingularName}.", e); |
| 155 | + } |
144 | 156 | }
|
145 | 157 | }
|
146 | 158 |
|
147 | 159 | quantity.Units = quantity.Units.Concat(unitsToAdd).ToArray();
|
148 | 160 | }
|
149 | 161 |
|
150 |
| - private static Localization[] GetLocalizationForPrefixUnit(Unit unit, int prefixIndex, PrefixInfo prefixInfo, string quantityName) |
| 162 | + /// <summary> |
| 163 | + /// Create unit abbreviations for a prefix unit, given a unit and the prefix. |
| 164 | + /// The unit abbreviations are either prefixed with the SI prefix or an explicitly configured abbreviation via <see cref="AbbreviationsForPrefixes"/>. |
| 165 | + /// </summary> |
| 166 | + private static Localization[] GetLocalizationForPrefixUnit(IEnumerable<Localization> localizations, PrefixInfo prefixInfo) |
151 | 167 | {
|
152 |
| - string[] GetUnitAbbreviationsForPrefix(Localization loc) |
| 168 | + return localizations.Select(loc => |
153 | 169 | {
|
154 |
| - // If no custom abbreviations are specified, prepend the default prefix to each unit abbreviation: kilo ("k") + meter ("m") => kilometer ("km") |
155 |
| - if (loc.AbbreviationsWithPrefixes == null || !loc.AbbreviationsWithPrefixes.Any()) |
| 170 | + if (loc.TryGetAbbreviationsForPrefix(prefixInfo.Prefix, out string[] unitAbbreviationsForPrefix)) |
156 | 171 | {
|
157 |
| - string prefix = prefixInfo.Abbreviation; |
158 |
| - return loc.Abbreviations.Select(unitAbbreviation => $"{prefix}{unitAbbreviation}").ToArray(); |
159 |
| - } |
160 |
| - |
161 |
| - /* |
162 |
| - Prepend prefix to all abbreviations of a unit. |
163 |
| - Some languages, like Russian, you can't simply prepend "k" for kilo prefix, so the prefix abbreviations must be explicitly defined |
164 |
| - with AbbreviationsWithPrefixes. |
165 |
| -
|
166 |
| - Example 1 - Torque.Newtonmeter has only a single abbreviation in Russian, so AbbreviationsWithPrefixes is an array of strings mapped to each prefix |
167 |
| -
|
168 |
| - { |
169 |
| - "SingularName": "NewtonMeter", |
170 |
| - "PluralName": "NewtonMeters", |
171 |
| - "FromUnitToBaseFunc": "x", |
172 |
| - "FromBaseToUnitFunc": "x", |
173 |
| - "Prefixes": [ "Kilo", "Mega" ], |
174 |
| - "Localization": [ |
175 |
| - { |
176 |
| - "Culture": "en-US", |
177 |
| - "Abbreviations": [ "N·m" ] |
178 |
| - }, |
179 |
| - { |
180 |
| - "Culture": "ru-RU", |
181 |
| - "Abbreviations": [ "Н·м" ], |
182 |
| - "AbbreviationsWithPrefixes": [ "кН·м", "МН·м" ] |
183 |
| - } |
184 |
| - ] |
185 |
| - }, |
186 |
| -
|
187 |
| - Example 2 - Duration.Second has 3 prefixes and 2 abbreviations in Russian, so AbbreviationsWithPrefixes is an array of 3 items where each |
188 |
| - represents the unit abbreviations for that prefix - typically a variant of those in "Abbreviations", but the counts don't have to match. |
189 |
| -
|
190 |
| - { |
191 |
| - "SingularName": "Second", |
192 |
| - "PluralName": "Seconds", |
193 |
| - "BaseUnits": { |
194 |
| - "T": "Second" |
195 |
| - }, |
196 |
| - "FromUnitToBaseFunc": "x", |
197 |
| - "FromBaseToUnitFunc": "x", |
198 |
| - "Prefixes": [ "Nano", "Micro", "Milli" ], |
199 |
| - "Localization": [ |
200 |
| - { |
201 |
| - "Culture": "en-US", |
202 |
| - "Abbreviations": [ "s", "sec", "secs", "second", "seconds" ] |
203 |
| - }, |
204 |
| - { |
205 |
| - "Culture": "ru-RU", |
206 |
| - "Abbreviations": [ "с", "сек" ], |
207 |
| - "AbbreviationsWithPrefixes": [ ["нс", "нсек"], ["мкс", "мксек"], ["мс", "мсек"] ] |
208 |
| - } |
209 |
| - ] |
210 |
| - } |
211 |
| - */ |
212 |
| - |
213 |
| - EnsureValidAbbreviationsWithPrefixes(loc, unit, quantityName); |
214 |
| - JToken abbreviationsForPrefix = loc.AbbreviationsWithPrefixes[prefixIndex]; |
215 |
| - switch (abbreviationsForPrefix.Type) |
216 |
| - { |
217 |
| - case JTokenType.Array: |
218 |
| - return abbreviationsForPrefix.ToObject<string[]>(); |
219 |
| - case JTokenType.String: |
220 |
| - return new[] {abbreviationsForPrefix.ToObject<string>()}; |
221 |
| - default: |
222 |
| - throw new NotSupportedException("Expect AbbreviationsWithPrefixes to be an array of strings or string arrays."); |
| 172 | + // Use explicitly defined prefix unit abbreviations |
| 173 | + return new Localization |
| 174 | + { |
| 175 | + Culture = loc.Culture, |
| 176 | + Abbreviations = unitAbbreviationsForPrefix, |
| 177 | + }; |
223 | 178 | }
|
224 |
| - } |
225 | 179 |
|
226 |
| - Localization WithPrefixes(Localization loc) |
227 |
| - { |
228 |
| - string[] unitAbbreviationsForPrefix = GetUnitAbbreviationsForPrefix(loc); |
| 180 | + // No prefix unit abbreviations are specified, so fall back to prepending the default SI prefix to each unit abbreviation: |
| 181 | + // kilo ("k") + meter ("m") => kilometer ("km") |
| 182 | + string prefix = prefixInfo.Abbreviation; |
| 183 | + unitAbbreviationsForPrefix = loc.Abbreviations.Select(unitAbbreviation => $"{prefix}{unitAbbreviation}").ToArray(); |
229 | 184 |
|
230 | 185 | return new Localization
|
231 | 186 | {
|
232 | 187 | Culture = loc.Culture,
|
233 | 188 | Abbreviations = unitAbbreviationsForPrefix,
|
234 | 189 | };
|
235 |
| - } |
236 |
| - |
237 |
| - return unit.Localization.Select(WithPrefixes).ToArray(); |
238 |
| - } |
239 |
| - |
240 |
| - private static void EnsureValidAbbreviationsWithPrefixes(Localization localization, Unit unit, string quantityName) |
241 |
| - { |
242 |
| - if (localization.AbbreviationsWithPrefixes.Length > 0 && |
243 |
| - localization.AbbreviationsWithPrefixes.Length != unit.Prefixes.Length) |
244 |
| - { |
245 |
| - throw new InvalidDataException( |
246 |
| - $"The Prefixes array length {unit.Prefixes.Length} does not match Localization.AbbreviationsWithPrefixes array length {localization.AbbreviationsWithPrefixes.Length} for {quantityName}.{unit.SingularName}"); |
247 |
| - } |
| 190 | + }).ToArray(); |
248 | 191 | }
|
249 | 192 | }
|
250 | 193 | }
|
0 commit comments