ICUBinary.java revision aacdd6f022693689b3bf76f70670711f3254a441
17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/* 27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 39fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * Copyright (C) 1996-2015, International Business Machines Corporation and 47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved. 57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl; 97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.DataOutputStream; 117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.File; 127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.FileInputStream; 137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.FileNotFoundException; 147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException; 157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.InputStream; 167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.nio.ByteBuffer; 177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.nio.ByteOrder; 187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.nio.channels.FileChannel; 197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList; 207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List; 217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException; 229fdd2feb5742e9f969903946b56584e4c8104e69Markus Schererimport java.util.Set; 237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ICUUncheckedIOException; 257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.VersionInfo; 267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic final class ICUBinary { 287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Reads the ICU .dat package file format. 307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Most methods do not modify the ByteBuffer in any way, 317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * not even its position or other state. 327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final class DatPackageReader { 347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * .dat package data format ID "CmnD". 367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int DATA_FORMAT = 0x436d6e44; 387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final class IsAcceptable implements Authenticate { 407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // @Override when we switch to Java 6 417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean isDataVersionAcceptable(byte version[]) { 427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return version[0] == 1; 437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final IsAcceptable IS_ACCEPTABLE = new IsAcceptable(); 467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Checks that the ByteBuffer contains a valid, usable ICU .dat package. 497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Moves the buffer position from 0 to after the data header. 507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 519fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer static boolean validate(ByteBuffer bytes) { 527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert readHeader(bytes, DATA_FORMAT, IS_ACCEPTABLE); 547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch (IOException ignored) { 557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int count = bytes.getInt(bytes.position()); // Do not move the position. 587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (count <= 0) { 597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // For each item, there is one ToC entry (8 bytes) and a name string 627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // and a data item of at least 16 bytes. 637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // (We assume no data item duplicate elimination for now.) 647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (bytes.position() + 4 + count * (8 + 16) > bytes.capacity()) { 657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!startsWithPackageName(bytes, getNameOffset(bytes, 0)) || 687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert !startsWithPackageName(bytes, getNameOffset(bytes, count - 1))) { 697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return true; 727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static boolean startsWithPackageName(ByteBuffer bytes, int start) { 757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Compare all but the trailing 'b' or 'l' which depends on the platform. 767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int length = ICUData.PACKAGE_NAME.length() - 1; 777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 0; i < length; ++i) { 787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (bytes.get(start + i) != ICUData.PACKAGE_NAME.charAt(i)) { 797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Check for 'b' or 'l' followed by '/'. 837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte c = bytes.get(start + length++); 847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if ((c != 'b' && c != 'l') || bytes.get(start + length) != '/') { 857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return true; 887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 909fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer static ByteBuffer getData(ByteBuffer bytes, CharSequence key) { 919fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer int index = binarySearch(bytes, key); 929fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (index >= 0) { 939fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer ByteBuffer data = bytes.duplicate(); 949fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer data.position(getDataOffset(bytes, index)); 959fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer data.limit(getDataOffset(bytes, index + 1)); 969fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return ICUBinary.sliceWithOrder(data); 979fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } else { 989fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return null; 999fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1009fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1019fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 1029fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer static void addBaseNamesInFolder(ByteBuffer bytes, String folder, String suffix, Set<String> names) { 1039fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer // Find the first data item name that starts with the folder name. 1049fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer int index = binarySearch(bytes, folder); 1059fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (index < 0) { 1069fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer index = ~index; // Normal: Otherwise the folder itself is the name of a data item. 1079fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1089fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 1099fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer int base = bytes.position(); 1109fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer int count = bytes.getInt(base); 1119fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer StringBuilder sb = new StringBuilder(); 1129fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer while (index < count && addBaseName(bytes, index, folder, suffix, sb, names)) { 1139fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer ++index; 1149fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1159fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1169fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 1179fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer private static int binarySearch(ByteBuffer bytes, CharSequence key) { 1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int base = bytes.position(); 1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int count = bytes.getInt(base); 1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Do a binary search for the key. 1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int start = 0; 1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int limit = count; 1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (start < limit) { 1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int mid = (start + limit) >>> 1; 1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int nameOffset = getNameOffset(bytes, mid); 1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Skip "icudt54b/". 1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert nameOffset += ICUData.PACKAGE_NAME.length() + 1; 1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int result = compareKeys(key, bytes, nameOffset); 1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (result < 0) { 1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert limit = mid; 1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (result > 0) { 1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert start = mid + 1; 1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // We found it! 1369fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return mid; 1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1399fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return ~start; // Not found or table is empty. 1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static int getNameOffset(ByteBuffer bytes, int index) { 1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int base = bytes.position(); 1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert 0 <= index && index < bytes.getInt(base); // count 1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The count integer (4 bytes) 1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // is followed by count (nameOffset, dataOffset) integer pairs (8 bytes per pair). 1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return base + bytes.getInt(base + 4 + index * 8); 1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static int getDataOffset(ByteBuffer bytes, int index) { 1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int base = bytes.position(); 1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int count = bytes.getInt(base); 1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (index == count) { 1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Return the limit of the last data item. 1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return bytes.capacity(); 1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert 0 <= index && index < count; 1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The count integer (4 bytes) 1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // is followed by count (nameOffset, dataOffset) integer pairs (8 bytes per pair). 1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The dataOffset follows the nameOffset (skip another 4 bytes). 1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return base + bytes.getInt(base + 4 + 4 + index * 8); 1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1639fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 1649fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer static boolean addBaseName(ByteBuffer bytes, int index, 1659fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer String folder, String suffix, StringBuilder sb, Set<String> names) { 1669fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer int offset = getNameOffset(bytes, index); 1679fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer // Skip "icudt54b/". 1689fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer offset += ICUData.PACKAGE_NAME.length() + 1; 1699fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (folder.length() != 0) { 1709fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer // Test name.startsWith(folder + '/'). 1719fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer for (int i = 0; i < folder.length(); ++i, ++offset) { 1729fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (bytes.get(offset) != folder.charAt(i)) { 1739fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return false; 1749fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1759fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1769fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (bytes.get(offset++) != '/') { 1779fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return false; 1789fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1799fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1809fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer // Collect the NUL-terminated name and test for a subfolder, then test for the suffix. 1819fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer sb.setLength(0); 1829fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer byte b; 1839fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer while ((b = bytes.get(offset++)) != 0) { 1849fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer char c = (char) b; 1859fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (c == '/') { 1869fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return true; // Skip subfolder contents. 1879fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1889fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer sb.append(c); 1899fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1909fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer int nameLimit = sb.length() - suffix.length(); 1919fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (sb.lastIndexOf(suffix, nameLimit) >= 0) { 1929fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer names.add(sb.substring(0, nameLimit)); 1939fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1949fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return true; 1959fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1989fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer private static abstract class DataFile { 1999fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer protected final String itemPath; 2009fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2019fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer DataFile(String item) { 2029fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer itemPath = item; 2039fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2049fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer @Override 2059fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer public String toString() { 2069fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return itemPath; 2079fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2089fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2099fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer abstract ByteBuffer getData(String requestedPath); 2109fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2129fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * @param folder The relative ICU data folder, like "" or "coll". 2139fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * @param suffix Usually ".res". 2149fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * @param names File base names relative to the folder are added without the suffix, 2159fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * for example "de_CH". 2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2179fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer abstract void addBaseNamesInFolder(String folder, String suffix, Set<String> names); 2189fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2199fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2209fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer private static final class SingleDataFile extends DataFile { 2219fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer private final File path; 2229fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2239fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer SingleDataFile(String item, File path) { 2249fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer super(item); 2259fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer this.path = path; 2269fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2279fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer @Override 2289fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer public String toString() { 2299fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return path.toString(); 2309fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2319fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2329fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer @Override 2339fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer ByteBuffer getData(String requestedPath) { 2349fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (requestedPath.equals(itemPath)) { 2359fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return mapFile(path); 2369fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } else { 2379fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return null; 2389fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2399fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2409fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2419fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer @Override 2429fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer void addBaseNamesInFolder(String folder, String suffix, Set<String> names) { 2439fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (itemPath.length() > folder.length() + suffix.length() && 2449fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer itemPath.startsWith(folder) && 2459fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer itemPath.endsWith(suffix) && 2469fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer itemPath.charAt(folder.length()) == '/' && 2479fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer itemPath.indexOf('/', folder.length() + 1) < 0) { 2489fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer names.add(itemPath.substring(folder.length() + 1, 2499fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer itemPath.length() - suffix.length())); 2509fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2519fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2529fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2539fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2549fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer private static final class PackageDataFile extends DataFile { 2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * .dat package bytes, or null if not a .dat package. 2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * position() is after the header. 2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Do not modify the position or other state, for thread safety. 2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2609fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer private final ByteBuffer pkgBytes; 2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2629fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer PackageDataFile(String item, ByteBuffer bytes) { 2639fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer super(item); 2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pkgBytes = bytes; 2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2669fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2679fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer @Override 2689fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer ByteBuffer getData(String requestedPath) { 2699fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return DatPackageReader.getData(pkgBytes, requestedPath); 2709fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 2719fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2729fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer @Override 2739fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer void addBaseNamesInFolder(String folder, String suffix, Set<String> names) { 2749fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer DatPackageReader.addBaseNamesInFolder(pkgBytes, folder, suffix, names); 2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2779fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final List<DataFile> icuDataFiles = new ArrayList<DataFile>(); 2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert static { 2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Normally com.ibm.icu.impl.ICUBinary.dataPath. 2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String dataPath = ICUConfig.get(ICUBinary.class.getName() + ".dataPath"); 2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (dataPath != null) { 2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert addDataFilesFromPath(dataPath, icuDataFiles); 2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static void addDataFilesFromPath(String dataPath, List<DataFile> files) { 2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Split the path and find files in each location. 2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // This splitting code avoids the regex pattern compilation in String.split() 2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // and its array allocation. 2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // (There is no simple by-character split() 2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // and the StringTokenizer "is discouraged in new code".) 2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int pathStart = 0; 2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (pathStart < dataPath.length()) { 2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int sepIndex = dataPath.indexOf(File.pathSeparatorChar, pathStart); 2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int pathLimit; 2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (sepIndex >= 0) { 2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pathLimit = sepIndex; 3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pathLimit = dataPath.length(); 3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String path = dataPath.substring(pathStart, pathLimit).trim(); 3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (path.endsWith(File.separator)) { 3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert path = path.substring(0, path.length() - 1); 3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (path.length() != 0) { 3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert addDataFilesFromFolder(new File(path), new StringBuilder(), icuDataFiles); 3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (sepIndex < 0) { 3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pathStart = sepIndex + 1; 3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static void addDataFilesFromFolder(File folder, StringBuilder itemPath, 3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert List<DataFile> dataFiles) { 3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert File[] files = folder.listFiles(); 3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (files == null || files.length == 0) { 3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; 3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int folderPathLength = itemPath.length(); 3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (folderPathLength > 0) { 3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The item path must use the ICU file separator character, 3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // not the platform-dependent File.separatorChar, 3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // so that the enumerated item paths match the paths requested by ICU code. 3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itemPath.append('/'); 3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++folderPathLength; 3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (File file : files) { 3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String fileName = file.getName(); 3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (fileName.endsWith(".txt")) { 3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert continue; 3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itemPath.append(fileName); 3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (file.isDirectory()) { 3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // TODO: Within a folder, put all single files before all .dat packages? 3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert addDataFilesFromFolder(file, itemPath, dataFiles); 3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (fileName.endsWith(".dat")) { 3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ByteBuffer pkgBytes = mapFile(file); 3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (pkgBytes != null && DatPackageReader.validate(pkgBytes)) { 3439fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer dataFiles.add(new PackageDataFile(itemPath.toString(), pkgBytes)); 3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 3469fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer dataFiles.add(new SingleDataFile(itemPath.toString(), file)); 3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itemPath.setLength(folderPathLength); 3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Compares the length-specified input key with the 3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * NUL-terminated table key. (ASCII) 3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert static int compareKeys(CharSequence key, ByteBuffer bytes, int offset) { 3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 0;; ++i, ++offset) { 3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int c2 = bytes.get(offset); 3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (c2 == 0) { 3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (i == key.length()) { 3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return 0; 3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return 1; // key > table key because key is longer. 3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (i == key.length()) { 3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return -1; // key < table key because key is shorter. 3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int diff = (int)key.charAt(i) - c2; 3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (diff != 0) { 3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return diff; 3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // public inner interface ------------------------------------------------ 3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Special interface for data authentication 3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static interface Authenticate 3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert { 3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Method used in ICUBinary.readHeader() to provide data format 3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * authentication. 3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param version version of the current data 3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return true if dataformat is an acceptable version, false otherwise 3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean isDataVersionAcceptable(byte version[]); 3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // public methods -------------------------------------------------------- 3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Loads an ICU binary data file and returns it as a ByteBuffer. 3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The buffer contents is normally read-only, but its position etc. can be modified. 3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param itemPath Relative ICU data item path, for example "root.res" or "coll/ucadata.icu". 3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return The data as a read-only ByteBuffer, 3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * or null if the resource could not be found. 4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static ByteBuffer getData(String itemPath) { 4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return getData(null, null, itemPath, false); 4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Loads an ICU binary data file and returns it as a ByteBuffer. 4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The buffer contents is normally read-only, but its position etc. can be modified. 4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param loader Used for loader.getResourceAsStream() unless the data is found elsewhere. 4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param resourceName Resource name for use with the loader. 4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param itemPath Relative ICU data item path, for example "root.res" or "coll/ucadata.icu". 4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return The data as a read-only ByteBuffer, 4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * or null if the resource could not be found. 4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static ByteBuffer getData(ClassLoader loader, String resourceName, String itemPath) { 4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return getData(loader, resourceName, itemPath, false); 4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Loads an ICU binary data file and returns it as a ByteBuffer. 4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The buffer contents is normally read-only, but its position etc. can be modified. 4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param itemPath Relative ICU data item path, for example "root.res" or "coll/ucadata.icu". 4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return The data as a read-only ByteBuffer. 4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws MissingResourceException if required==true and the resource could not be found 4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static ByteBuffer getRequiredData(String itemPath) { 4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return getData(null, null, itemPath, true); 4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Loads an ICU binary data file and returns it as a ByteBuffer. 4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The buffer contents is normally read-only, but its position etc. can be modified. 4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param loader Used for loader.getResourceAsStream() unless the data is found elsewhere. 4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param resourceName Resource name for use with the loader. 4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param itemPath Relative ICU data item path, for example "root.res" or "coll/ucadata.icu". 4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return The data as a read-only ByteBuffer. 4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws MissingResourceException if required==true and the resource could not be found 4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert// public static ByteBuffer getRequiredData(ClassLoader loader, String resourceName, 4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert// String itemPath) { 4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert// return getData(loader, resourceName, itemPath, true); 4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert// } 4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Loads an ICU binary data file and returns it as a ByteBuffer. 4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The buffer contents is normally read-only, but its position etc. can be modified. 4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param loader Used for loader.getResourceAsStream() unless the data is found elsewhere. 4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param resourceName Resource name for use with the loader. 4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param itemPath Relative ICU data item path, for example "root.res" or "coll/ucadata.icu". 4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param required If the resource cannot be found, 4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * this method returns null (!required) or throws an exception (required). 4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return The data as a read-only ByteBuffer, 4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * or null if required==false and the resource could not be found. 4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws MissingResourceException if required==true and the resource could not be found 4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static ByteBuffer getData(ClassLoader loader, String resourceName, 4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String itemPath, boolean required) { 4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ByteBuffer bytes = getDataFromFile(itemPath); 4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (bytes != null) { 4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return bytes; 4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (loader == null) { 4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert loader = ICUData.class.getClassLoader(); 4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (resourceName == null) { 4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert resourceName = ICUData.ICU_BASE_NAME + '/' + itemPath; 4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 471aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert ByteBuffer buffer = null; 4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 473aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert @SuppressWarnings("resource") // Closed by getByteBufferFromInputStreamAndCloseStream(). 474aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert InputStream is = ICUData.getStream(loader, resourceName, required); 475aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert if (is == null) { 476aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert return null; 477aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert } 478aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert buffer = getByteBufferFromInputStreamAndCloseStream(is); 4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch (IOException e) { 4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new ICUUncheckedIOException(e); 4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 482aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert return buffer; 4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static ByteBuffer getDataFromFile(String itemPath) { 4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (DataFile dataFile : icuDataFiles) { 4879fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer ByteBuffer data = dataFile.getData(itemPath); 4889fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer if (data != null) { 4899fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer return data; 4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 495aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert @SuppressWarnings("resource") // Closing a file closes its channel. 4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static ByteBuffer mapFile(File path) { 4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert FileInputStream file; 4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert file = new FileInputStream(path); 5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert FileChannel channel = file.getChannel(); 501aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert ByteBuffer bytes = null; 502aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert try { 503aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert bytes = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); 504aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert } finally { 505aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert file.close(); 506aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert } 5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return bytes; 508aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert } catch (FileNotFoundException ignored) { 5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert System.err.println(ignored); 5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch (IOException ignored) { 5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert System.err.println(ignored); 5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 5179fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * @param folder The relative ICU data folder, like "" or "coll". 5189fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * @param suffix Usually ".res". 5199fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * @param names File base names relative to the folder are added without the suffix, 5209fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer * for example "de_CH". 5219fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer */ 5229fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer public static void addBaseNamesInFileFolder(String folder, String suffix, Set<String> names) { 5239fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer for (DataFile dataFile : icuDataFiles) { 5249fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer dataFile.addBaseNamesInFolder(folder, suffix, names); 5259fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 5269fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer } 5279fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer 5289fdd2feb5742e9f969903946b56584e4c8104e69Markus Scherer /** 5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Same as readHeader(), but returns a VersionInfo rather than a compact int. 5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static VersionInfo readHeaderAndDataVersion(ByteBuffer bytes, 5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int dataFormat, 5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Authenticate authenticate) 5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throws IOException { 5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return getVersionInfoFromCompactInt(readHeader(bytes, dataFormat, authenticate)); 5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Reads an ICU data header, checks the data format, and returns the data version. 5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Assumes that the ByteBuffer position is 0 on input. 5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The buffer byte order is set according to the data. 5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The buffer position is advanced past the header (including UDataInfo and comment). 5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>See C++ ucmndata.h and unicode/udata.h. 5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return dataVersion 5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IOException if this is not a valid ICU data item of the expected dataFormat 5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static int readHeader(ByteBuffer bytes, int dataFormat, Authenticate authenticate) 5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throws IOException { 5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert bytes.position() == 0; 5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte magic1 = bytes.get(2); 5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte magic2 = bytes.get(3); 5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (magic1 != MAGIC1 || magic2 != MAGIC2) { 5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_); 5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte isBigEndian = bytes.get(8); 5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte charsetFamily = bytes.get(9); 5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte sizeofUChar = bytes.get(10); 5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (isBigEndian < 0 || 1 < isBigEndian || 5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert charsetFamily != CHAR_SET_ || sizeofUChar != CHAR_SIZE_) { 5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IOException(HEADER_AUTHENTICATION_FAILED_); 5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes.order(isBigEndian != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); 5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int headerSize = bytes.getChar(0); 5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int sizeofUDataInfo = bytes.getChar(4); 5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (sizeofUDataInfo < 20 || headerSize < (sizeofUDataInfo + 4)) { 5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IOException("Internal Error: Header size error"); 5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // TODO: Change Authenticate to take int major, int minor, int milli, int micro 5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // to avoid array allocation. 5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte[] formatVersion = new byte[] { 5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes.get(16), bytes.get(17), bytes.get(18), bytes.get(19) 5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert }; 5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (bytes.get(12) != (byte)(dataFormat >> 24) || 5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes.get(13) != (byte)(dataFormat >> 16) || 5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes.get(14) != (byte)(dataFormat >> 8) || 5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes.get(15) != (byte)dataFormat || 5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (authenticate != null && !authenticate.isDataVersionAcceptable(formatVersion))) { 5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IOException(HEADER_AUTHENTICATION_FAILED_ + 5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String.format("; data format %02x%02x%02x%02x, format version %d.%d.%d.%d", 5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes.get(12), bytes.get(13), bytes.get(14), bytes.get(15), 5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatVersion[0] & 0xff, formatVersion[1] & 0xff, 5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatVersion[2] & 0xff, formatVersion[3] & 0xff)); 5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes.position(headerSize); 5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return // dataVersion 5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ((int)bytes.get(20) << 24) | 5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ((bytes.get(21) & 0xff) << 16) | 5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ((bytes.get(22) & 0xff) << 8) | 5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (bytes.get(23) & 0xff); 5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Writes an ICU data header. 6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Does not write a copyright string. 6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return The length of the header (number of bytes written). 6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IOException from the DataOutputStream 6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static int writeHeader(int dataFormat, int formatVersion, int dataVersion, 6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DataOutputStream dos) throws IOException { 6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ucmndata.h MappedData 6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeChar(32); // headerSize 6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeByte(MAGIC1); 6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeByte(MAGIC2); 6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // unicode/udata.h UDataInfo 6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeChar(20); // sizeof(UDataInfo) 6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeChar(0); // reservedWord 6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeByte(1); // isBigEndian 6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeByte(CHAR_SET_); // charsetFamily 6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeByte(CHAR_SIZE_); // sizeofUChar 6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeByte(0); // reservedByte 6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeInt(dataFormat); 6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeInt(formatVersion); 6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeInt(dataVersion); 6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // 8 bytes padding for 32 bytes headerSize (multiple of 16). 6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dos.writeLong(0); 6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert dos.size() == 32; 6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return 32; 6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static void skipBytes(ByteBuffer bytes, int skipLength) { 6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (skipLength > 0) { 6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes.position(bytes.position() + skipLength); 6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Same as ByteBuffer.slice() plus preserving the byte order. 6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static ByteBuffer sliceWithOrder(ByteBuffer bytes) { 6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ByteBuffer b = bytes.slice(); 6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return b.order(bytes.order()); 6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Reads the entire contents from the stream into a byte array 6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and wraps it into a ByteBuffer. Closes the InputStream at the end. 6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 645aacdd6f022693689b3bf76f70670711f3254a441Fredrik Roubert public static ByteBuffer getByteBufferFromInputStreamAndCloseStream(InputStream is) throws IOException { 6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int avail = is.available(); 6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte[] bytes = new byte[avail]; 6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert readFully(is, bytes, 0, avail); 6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while((avail = is.available()) != 0) { 6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // TODO Java 6 replace new byte[] and arraycopy(): byte[] newBytes = Arrays.copyOf(bytes, bytes.length + avail); 6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert byte[] newBytes = new byte[bytes.length + avail]; 6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert System.arraycopy(bytes, 0, newBytes, 0, bytes.length); 6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert readFully(is, newBytes, bytes.length, avail); 6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bytes = newBytes; 6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return ByteBuffer.wrap(bytes); 6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } finally { 6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert is.close(); 6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static void readFully(InputStream is, byte[] bytes, int offset, int avail) 6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throws IOException { 6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (avail > 0) { 6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int numRead = is.read(bytes, offset, avail); 6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert numRead > 0; 6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offset += numRead; 6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert avail -= numRead; 6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns a VersionInfo for the bytes in the compact version integer. 6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static VersionInfo getVersionInfoFromCompactInt(int version) { 6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return VersionInfo.getInstance( 6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert version >>> 24, (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); 6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns an array of the bytes in the compact version integer. 6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static byte[] getVersionByteArrayFromCompactInt(int version) { 6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new byte[] { 6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (byte)(version >> 24), 6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (byte)(version >> 16), 6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (byte)(version >> 8), 6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (byte)(version) 6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert }; 6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // private variables ------------------------------------------------- 6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Magic numbers to authenticate the data file 6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final byte MAGIC1 = (byte)0xda; 6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final byte MAGIC2 = (byte)0x27; 7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * File format authentication values 7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final byte CHAR_SET_ = 0; 7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final byte CHAR_SIZE_ = 2; 7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Error messages 7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final String MAGIC_NUMBER_AUTHENTICATION_FAILED_ = 7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "ICU data file error: Not an ICU data file"; 7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final String HEADER_AUTHENTICATION_FAILED_ = 7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "ICU data file error: Header authentication failed, please check if you have a valid ICU data file"; 7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert} 715