Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/make/src/classes/build/tools/generatecurrencydata/GenerateCurrencyData.java
32287 views
1
/*
2
* Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package build.tools.generatecurrencydata;
27
28
import java.io.IOException;
29
import java.io.FileNotFoundException;
30
import java.io.DataOutputStream;
31
import java.io.FileOutputStream;
32
import java.text.SimpleDateFormat;
33
import java.util.Date;
34
import java.util.HashMap;
35
import java.util.Locale;
36
import java.util.Properties;
37
import java.util.TimeZone;
38
39
/**
40
* Reads currency data in properties format from the file specified in the
41
* command line and generates a binary data file as specified in the command line.
42
*
43
* Output of this tool is a binary file that contains the data in
44
* the following order:
45
*
46
* - magic number (int): always 0x43757244 ('CurD')
47
* - formatVersion (int)
48
* - dataVersion (int)
49
* - mainTable (int[26*26])
50
* - specialCaseCount (int)
51
* - specialCaseCutOverTimes (long[specialCaseCount])
52
* - specialCaseOldCurrencies (String[specialCaseCount])
53
* - specialCaseNewCurrencies (String[specialCaseCount])
54
* - specialCaseOldCurrenciesDefaultFractionDigits (int[specialCaseCount])
55
* - specialCaseNewCurrenciesDefaultFractionDigits (int[specialCaseCount])
56
* - specialCaseOldCurrenciesNumericCode (int[specialCaseCount])
57
* - specialCaseNewCurrenciesNumericCode (int[specialCaseCount])
58
* - otherCurrenciesCount (int)
59
* - otherCurrencies (String)
60
* - otherCurrenciesDefaultFractionDigits (int[otherCurrenciesCount])
61
* - otherCurrenciesNumericCode (int[otherCurrenciesCount])
62
*
63
* See CurrencyData.properties for the input format description and
64
* Currency.java for the format descriptions of the generated tables.
65
*/
66
public class GenerateCurrencyData {
67
68
private static DataOutputStream out;
69
70
// input data: currency data obtained from properties on input stream
71
private static Properties currencyData;
72
private static String formatVersion;
73
private static String dataVersion;
74
private static String validCurrencyCodes;
75
76
// handy constants - must match definitions in java.util.Currency
77
// magic number
78
private static final int MAGIC_NUMBER = 0x43757244;
79
// number of characters from A to Z
80
private static final int A_TO_Z = ('Z' - 'A') + 1;
81
// entry for invalid country codes
82
private static final int INVALID_COUNTRY_ENTRY = 0x0000007F;
83
// entry for countries without currency
84
private static final int COUNTRY_WITHOUT_CURRENCY_ENTRY = 0x00000200;
85
// mask for simple case country entries
86
private static final int SIMPLE_CASE_COUNTRY_MASK = 0x00000000;
87
// mask for simple case country entry final character
88
private static final int SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK = 0x0000001F;
89
// mask for simple case country entry default currency digits
90
private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK = 0x000001E0;
91
// shift count for simple case country entry default currency digits
92
private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT = 5;
93
// maximum number for simple case country entry default currency digits
94
private static final int SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS = 9;
95
// mask for special case country entries
96
private static final int SPECIAL_CASE_COUNTRY_MASK = 0x00000200;
97
// mask for special case country index
98
private static final int SPECIAL_CASE_COUNTRY_INDEX_MASK = 0x0000001F;
99
// delta from entry index component in main table to index into special case tables
100
private static final int SPECIAL_CASE_COUNTRY_INDEX_DELTA = 1;
101
// mask for distinguishing simple and special case countries
102
private static final int COUNTRY_TYPE_MASK = SIMPLE_CASE_COUNTRY_MASK | SPECIAL_CASE_COUNTRY_MASK;
103
// mask for the numeric code of the currency
104
private static final int NUMERIC_CODE_MASK = 0x000FFC00;
105
// shift count for the numeric code of the currency
106
private static final int NUMERIC_CODE_SHIFT = 10;
107
108
// generated data
109
private static int[] mainTable = new int[A_TO_Z * A_TO_Z];
110
111
private static final int maxSpecialCases = 30;
112
private static int specialCaseCount = 0;
113
private static long[] specialCaseCutOverTimes = new long[maxSpecialCases];
114
private static String[] specialCaseOldCurrencies = new String[maxSpecialCases];
115
private static String[] specialCaseNewCurrencies = new String[maxSpecialCases];
116
private static int[] specialCaseOldCurrenciesDefaultFractionDigits = new int[maxSpecialCases];
117
private static int[] specialCaseNewCurrenciesDefaultFractionDigits = new int[maxSpecialCases];
118
private static int[] specialCaseOldCurrenciesNumericCode = new int[maxSpecialCases];
119
private static int[] specialCaseNewCurrenciesNumericCode = new int[maxSpecialCases];
120
121
private static final int maxOtherCurrencies = 128;
122
private static int otherCurrenciesCount = 0;
123
private static StringBuffer otherCurrencies = new StringBuffer();
124
private static int[] otherCurrenciesDefaultFractionDigits = new int[maxOtherCurrencies];
125
private static int[] otherCurrenciesNumericCode= new int[maxOtherCurrencies];
126
127
// date format for parsing cut-over times
128
private static SimpleDateFormat format;
129
130
// Minor Units
131
private static String[] currenciesWithDefinedMinorUnitDecimals =
132
new String[SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS + 1];
133
private static String currenciesWithMinorUnitsUndefined;
134
135
public static void main(String[] args) {
136
137
// Look for "-o outputfilename" option
138
if ( args.length == 2 && args[0].equals("-o") ) {
139
try {
140
out = new DataOutputStream(new FileOutputStream(args[1]));
141
} catch ( FileNotFoundException e ) {
142
System.err.println("Error: " + e.getMessage());
143
e.printStackTrace(System.err);
144
System.exit(1);
145
}
146
} else {
147
System.err.println("Error: Illegal arg count");
148
System.exit(1);
149
}
150
151
format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
152
format.setTimeZone(TimeZone.getTimeZone("GMT"));
153
format.setLenient(false);
154
155
try {
156
readInput();
157
buildMainAndSpecialCaseTables();
158
buildOtherTables();
159
writeOutput();
160
out.flush();
161
out.close();
162
} catch (Exception e) {
163
System.err.println("Error: " + e.getMessage());
164
e.printStackTrace(System.err);
165
System.exit(1);
166
}
167
}
168
169
private static void readInput() throws IOException {
170
currencyData = new Properties();
171
currencyData.load(System.in);
172
173
// initialize other lookup strings
174
formatVersion = (String) currencyData.get("formatVersion");
175
dataVersion = (String) currencyData.get("dataVersion");
176
validCurrencyCodes = (String) currencyData.get("all");
177
for (int i = 0; i <= SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS; i++) {
178
currenciesWithDefinedMinorUnitDecimals[i]
179
= (String) currencyData.get("minor"+i);
180
}
181
currenciesWithMinorUnitsUndefined = (String) currencyData.get("minorUndefined");
182
if (formatVersion == null ||
183
dataVersion == null ||
184
validCurrencyCodes == null ||
185
currenciesWithMinorUnitsUndefined == null) {
186
throw new NullPointerException("not all required data is defined in input");
187
}
188
}
189
190
private static void buildMainAndSpecialCaseTables() throws Exception {
191
for (int first = 0; first < A_TO_Z; first++) {
192
for (int second = 0; second < A_TO_Z; second++) {
193
char firstChar = (char) ('A' + first);
194
char secondChar = (char) ('A' + second);
195
String countryCode = (new StringBuffer()).append(firstChar).append(secondChar).toString();
196
String currencyInfo = (String) currencyData.get(countryCode);
197
int tableEntry = 0;
198
if (currencyInfo == null) {
199
// no entry -> must be invalid ISO 3166 country code
200
tableEntry = INVALID_COUNTRY_ENTRY;
201
} else {
202
int length = currencyInfo.length();
203
if (length == 0) {
204
// special case: country without currency
205
tableEntry = COUNTRY_WITHOUT_CURRENCY_ENTRY;
206
} else if (length == 3) {
207
// valid currency
208
if (currencyInfo.charAt(0) == firstChar && currencyInfo.charAt(1) == secondChar) {
209
checkCurrencyCode(currencyInfo);
210
int digits = getDefaultFractionDigits(currencyInfo);
211
if (digits < 0 || digits > SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS) {
212
throw new RuntimeException("fraction digits out of range for " + currencyInfo);
213
}
214
int numericCode= getNumericCode(currencyInfo);
215
if (numericCode < 0 || numericCode >= 1000 ) {
216
throw new RuntimeException("numeric code out of range for " + currencyInfo);
217
}
218
tableEntry = SIMPLE_CASE_COUNTRY_MASK
219
| (currencyInfo.charAt(2) - 'A')
220
| (digits << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT)
221
| (numericCode << NUMERIC_CODE_SHIFT);
222
} else {
223
tableEntry = SPECIAL_CASE_COUNTRY_MASK | (makeSpecialCaseEntry(currencyInfo) + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
224
}
225
} else {
226
tableEntry = SPECIAL_CASE_COUNTRY_MASK | (makeSpecialCaseEntry(currencyInfo) + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
227
}
228
}
229
mainTable[first * A_TO_Z + second] = tableEntry;
230
}
231
}
232
}
233
234
private static int getDefaultFractionDigits(String currencyCode) {
235
for (int i = 0; i <= SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS; i++) {
236
if (currenciesWithDefinedMinorUnitDecimals[i] != null &&
237
currenciesWithDefinedMinorUnitDecimals[i].indexOf(currencyCode) != -1) {
238
return i;
239
}
240
}
241
242
if (currenciesWithMinorUnitsUndefined.indexOf(currencyCode) != -1) {
243
return -1;
244
} else {
245
return 2;
246
}
247
}
248
249
private static int getNumericCode(String currencyCode) {
250
int index = validCurrencyCodes.indexOf(currencyCode);
251
String numericCode = validCurrencyCodes.substring(index + 3, index + 6);
252
return Integer.parseInt(numericCode);
253
}
254
255
static HashMap<String, Integer> specialCaseMap = new HashMap<>();
256
257
private static int makeSpecialCaseEntry(String currencyInfo) throws Exception {
258
Integer oldEntry = specialCaseMap.get(currencyInfo);
259
if (oldEntry != null) {
260
return oldEntry.intValue();
261
}
262
if (specialCaseCount == maxSpecialCases) {
263
throw new RuntimeException("too many special cases");
264
}
265
if (currencyInfo.length() == 3) {
266
checkCurrencyCode(currencyInfo);
267
specialCaseCutOverTimes[specialCaseCount] = Long.MAX_VALUE;
268
specialCaseOldCurrencies[specialCaseCount] = currencyInfo;
269
specialCaseOldCurrenciesDefaultFractionDigits[specialCaseCount] = getDefaultFractionDigits(currencyInfo);
270
specialCaseOldCurrenciesNumericCode[specialCaseCount] = getNumericCode(currencyInfo);
271
specialCaseNewCurrencies[specialCaseCount] = null;
272
specialCaseNewCurrenciesDefaultFractionDigits[specialCaseCount] = 0;
273
specialCaseNewCurrenciesNumericCode[specialCaseCount] = 0;
274
} else {
275
int length = currencyInfo.length();
276
if (currencyInfo.charAt(3) != ';' ||
277
currencyInfo.charAt(length - 4) != ';') {
278
throw new RuntimeException("invalid currency info: " + currencyInfo);
279
}
280
String oldCurrency = currencyInfo.substring(0, 3);
281
String newCurrency = currencyInfo.substring(length - 3, length);
282
checkCurrencyCode(oldCurrency);
283
checkCurrencyCode(newCurrency);
284
String timeString = currencyInfo.substring(4, length - 4);
285
long time = format.parse(timeString).getTime();
286
if (Math.abs(time - System.currentTimeMillis()) > ((long) 10) * 365 * 24 * 60 * 60 * 1000) {
287
throw new RuntimeException("time is more than 10 years from present: " + time);
288
}
289
specialCaseCutOverTimes[specialCaseCount] = time;
290
specialCaseOldCurrencies[specialCaseCount] = oldCurrency;
291
specialCaseOldCurrenciesDefaultFractionDigits[specialCaseCount] = getDefaultFractionDigits(oldCurrency);
292
specialCaseOldCurrenciesNumericCode[specialCaseCount] = getNumericCode(oldCurrency);
293
specialCaseNewCurrencies[specialCaseCount] = newCurrency;
294
specialCaseNewCurrenciesDefaultFractionDigits[specialCaseCount] = getDefaultFractionDigits(newCurrency);
295
specialCaseNewCurrenciesNumericCode[specialCaseCount] = getNumericCode(newCurrency);
296
}
297
specialCaseMap.put(currencyInfo, new Integer(specialCaseCount));
298
return specialCaseCount++;
299
}
300
301
private static void buildOtherTables() {
302
if (validCurrencyCodes.length() % 7 != 6) {
303
throw new RuntimeException("\"all\" entry has incorrect size");
304
}
305
for (int i = 0; i < (validCurrencyCodes.length() + 1) / 7; i++) {
306
if (i > 0 && validCurrencyCodes.charAt(i * 7 - 1) != '-') {
307
throw new RuntimeException("incorrect separator in \"all\" entry");
308
}
309
String currencyCode = validCurrencyCodes.substring(i * 7, i * 7 + 3);
310
int numericCode = Integer.parseInt(
311
validCurrencyCodes.substring(i * 7 + 3, i * 7 + 6));
312
checkCurrencyCode(currencyCode);
313
int tableEntry = mainTable[(currencyCode.charAt(0) - 'A') * A_TO_Z + (currencyCode.charAt(1) - 'A')];
314
if (tableEntry == INVALID_COUNTRY_ENTRY ||
315
(tableEntry & SPECIAL_CASE_COUNTRY_MASK) != 0 ||
316
(tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) != (currencyCode.charAt(2) - 'A')) {
317
if (otherCurrenciesCount == maxOtherCurrencies) {
318
throw new RuntimeException("too many other currencies");
319
}
320
if (otherCurrencies.length() > 0) {
321
otherCurrencies.append('-');
322
}
323
otherCurrencies.append(currencyCode);
324
otherCurrenciesDefaultFractionDigits[otherCurrenciesCount] = getDefaultFractionDigits(currencyCode);
325
otherCurrenciesNumericCode[otherCurrenciesCount] = getNumericCode(currencyCode);
326
otherCurrenciesCount++;
327
}
328
}
329
}
330
331
private static void checkCurrencyCode(String currencyCode) {
332
if (currencyCode.length() != 3) {
333
throw new RuntimeException("illegal length for currency code: " + currencyCode);
334
}
335
for (int i = 0; i < 3; i++) {
336
char aChar = currencyCode.charAt(i);
337
if ((aChar < 'A' || aChar > 'Z') && !currencyCode.equals("XB5")) {
338
throw new RuntimeException("currency code contains illegal character: " + currencyCode);
339
}
340
}
341
if (validCurrencyCodes.indexOf(currencyCode) == -1) {
342
throw new RuntimeException("currency code not listed as valid: " + currencyCode);
343
}
344
}
345
346
private static void writeOutput() throws IOException {
347
out.writeInt(MAGIC_NUMBER);
348
out.writeInt(Integer.parseInt(formatVersion));
349
out.writeInt(Integer.parseInt(dataVersion));
350
writeIntArray(mainTable, mainTable.length);
351
out.writeInt(specialCaseCount);
352
writeLongArray(specialCaseCutOverTimes, specialCaseCount);
353
writeStringArray(specialCaseOldCurrencies, specialCaseCount);
354
writeStringArray(specialCaseNewCurrencies, specialCaseCount);
355
writeIntArray(specialCaseOldCurrenciesDefaultFractionDigits, specialCaseCount);
356
writeIntArray(specialCaseNewCurrenciesDefaultFractionDigits, specialCaseCount);
357
writeIntArray(specialCaseOldCurrenciesNumericCode, specialCaseCount);
358
writeIntArray(specialCaseNewCurrenciesNumericCode, specialCaseCount);
359
out.writeInt(otherCurrenciesCount);
360
out.writeUTF(otherCurrencies.toString());
361
writeIntArray(otherCurrenciesDefaultFractionDigits, otherCurrenciesCount);
362
writeIntArray(otherCurrenciesNumericCode, otherCurrenciesCount);
363
}
364
365
private static void writeIntArray(int[] ia, int count) throws IOException {
366
for (int i = 0; i < count; i ++) {
367
out.writeInt(ia[i]);
368
}
369
}
370
371
private static void writeLongArray(long[] la, int count) throws IOException {
372
for (int i = 0; i < count; i ++) {
373
out.writeLong(la[i]);
374
}
375
}
376
377
private static void writeStringArray(String[] sa, int count) throws IOException {
378
for (int i = 0; i < count; i ++) {
379
String str = (sa[i] != null) ? sa[i] : "";
380
out.writeUTF(str);
381
}
382
}
383
}
384
385