39 |
package java.lang; |
package java.lang; |
40 |
|
|
41 |
import java.io.Serializable; |
import java.io.Serializable; |
42 |
import java.io.File; |
import gnu.java.lang.CharData; |
|
import java.io.DataInputStream; |
|
|
import java.io.FileInputStream; |
|
|
import java.io.RandomAccessFile; |
|
|
import java.io.IOException; |
|
|
import gnu.java.lang.ClassLoaderHelper; |
|
43 |
|
|
44 |
/** |
/** |
45 |
* Wrapper class for the primitive char data type. In addition, |
* Wrapper class for the primitive char data type. In addition, this class |
46 |
* this class allows one to retrieve property information and |
* allows one to retrieve property information and perform transformations |
47 |
* perform transformations on the 57,707 defined characters in the |
* on the 57,707 defined characters in the Unicode Standard, Version 3.0.0. |
48 |
* Unicode Standard, Version 3.0.0. java.lang.Character is designed |
* java.lang.Character is designed to be very dynamic, and as such, it |
49 |
* to be very dynamic, and as such, it retrieves information on |
* retrieves information on the Unicode character set from a separate |
50 |
* the Unicode character set from a separate database, which |
* database, gnu.java.lang.CharData, which can be easily upgraded. |
|
* can be easily upgraded. |
|
51 |
* |
* |
52 |
* <p>For predicates, boundaries are used to describe |
* <p>For predicates, boundaries are used to describe |
53 |
* the set of characters for which the method will return true. |
* the set of characters for which the method will return true. |
84 |
*/ |
*/ |
85 |
protected Subset(String name) |
protected Subset(String name) |
86 |
{ |
{ |
87 |
|
// Note that name.toString() is name, unless name was null. |
88 |
this.name = name.toString(); |
this.name = name.toString(); |
89 |
} |
} |
90 |
|
|
127 |
* A family of character subsets in the Unicode specification. A character |
* A family of character subsets in the Unicode specification. A character |
128 |
* is in at most one of these blocks. |
* is in at most one of these blocks. |
129 |
* |
* |
130 |
* This class was generated by doc/unicode/unicode-blocks.pl. |
* This inner class was generated by doc/unicode/unicode-blocks.pl. |
131 |
* |
* |
132 |
|
* @author doc/unicode/unicode-blocks.pl (written by Eric Blake) |
133 |
* @since 1.2 |
* @since 1.2 |
134 |
*/ |
*/ |
135 |
public static final class UnicodeBlock extends Subset |
public static final class UnicodeBlock extends Subset |
1375 |
*/ |
*/ |
1376 |
public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18; |
public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18; |
1377 |
|
|
1378 |
/** Stores unicode blocks in gnu/java/locale/block.uni. */ |
/** |
1379 |
private static final Block blocks[]; |
* Stores unicode block offset lookup table. Exploit package visibility of |
1380 |
|
* String.value to avoid copying the array. |
1381 |
/** Stores titlecase information in gnu/java/locale/titlecase.uni. */ |
* @see #readChar(char) |
1382 |
private static final char tcs[][]; |
* @see CharData#BLOCKS |
1383 |
|
*/ |
1384 |
/** Stores unicode data in gnu/java/locale/character.uni. */ |
private static final char[] blocks = CharData.BLOCKS.value; |
|
private static final byte[] unicodeData; |
|
1385 |
|
|
1386 |
/** Caches the most recently used CharAttr. */ |
/** |
1387 |
private static CharAttr cachedCharAttr; |
* Stores unicode attribute offset lookup table. Exploit package visibility |
1388 |
|
* of String.value to avoid copying the array. |
1389 |
|
* @see CharData#DATA |
1390 |
|
*/ |
1391 |
|
private static final char[] data = CharData.DATA.value; |
1392 |
|
|
1393 |
/** The size of a block in gnu/java/locale/block.uni. */ |
/** |
1394 |
private static final int BLOCK_SIZE = 6; |
* Stores unicode numeric value attribute table. Exploit package visibility |
1395 |
|
* of String.value to avoid copying the array. |
1396 |
|
* @see CharData#NUM_VALUE |
1397 |
|
*/ |
1398 |
|
private static final char[] numValue = CharData.NUM_VALUE.value; |
1399 |
|
|
1400 |
/** The size of an attribute in gnu/java/locale/character.uni. */ |
/** |
1401 |
private static final int ATTR_SIZE = 8; |
* Stores unicode uppercase attribute table. Exploit package visibility |
1402 |
|
* of String.value to avoid copying the array. |
1403 |
|
* @see CharData#UPPER |
1404 |
|
*/ |
1405 |
|
private static final char[] upper = CharData.UPPER.value; |
1406 |
|
|
1407 |
/** |
/** |
1408 |
* Open up the Unicode attribute database and read the index into memory. |
* Stores unicode lowercase attribute table. Exploit package visibility |
1409 |
* These files were generated by doc/unicode/unicode-muncher.pl. |
* of String.value to avoid copying the array. |
1410 |
|
* @see CharData#LOWER |
1411 |
*/ |
*/ |
1412 |
static |
private static final char[] lower = CharData.LOWER.value; |
|
{ |
|
|
File cFile = ClassLoaderHelper.getSystemResourceAsFile( |
|
|
"/gnu/java/locale/character.uni"); |
|
|
File blockFile = ClassLoaderHelper.getSystemResourceAsFile( |
|
|
"/gnu/java/locale/block.uni"); |
|
|
File tcFile = ClassLoaderHelper.getSystemResourceAsFile( |
|
|
"/gnu/java/locale/titlecase.uni"); |
|
|
if (cFile == null || blockFile == null || tcFile == null) |
|
|
throw new InternalError("Cannot locate Unicode attribute database."); |
|
1413 |
|
|
1414 |
blocks = new Block[(int) blockFile.length() / BLOCK_SIZE]; |
/** |
1415 |
try |
* Stores unicode direction attribute table. Exploit package visibility |
1416 |
{ |
* of String.value to avoid copying the array. |
1417 |
DataInputStream blockIS |
* @see CharData#DIRECTION |
1418 |
= new DataInputStream(new FileInputStream(blockFile)); |
*/ |
1419 |
for (int i = 0; i < blocks.length; i++) |
private static final char[] direction = CharData.DIRECTION.value; |
|
{ |
|
|
char start = blockIS.readChar(); |
|
|
char end = blockIS.readChar(); |
|
|
short offset = blockIS.readShort(); |
|
|
blocks[i] = new Block(start, end, offset); |
|
|
} |
|
|
} |
|
|
catch (IOException e) |
|
|
{ |
|
|
throw new InternalError("Error reading block file: " + e); |
|
|
} |
|
1420 |
|
|
1421 |
tcs = new char[(int) tcFile.length() / 4][2]; |
/** |
1422 |
try |
* Stores unicode titlecase table. Exploit package visibility of |
1423 |
{ |
* String.value to avoid copying the array. |
1424 |
DataInputStream tcIS |
* @see CharData#TITLE |
1425 |
= new DataInputStream(new FileInputStream(tcFile)); |
*/ |
1426 |
for (int i = 0; i < tcs.length; i++) |
private static final char[] title = CharData.TITLE.value; |
|
{ |
|
|
tcs[i][0] = tcIS.readChar(); |
|
|
tcs[i][1] = tcIS.readChar(); |
|
|
} |
|
|
} |
|
|
catch (IOException e) |
|
|
{ |
|
|
throw new InternalError("Error reading titlecase file: " + e); |
|
|
} |
|
1427 |
|
|
1428 |
try |
/** |
1429 |
{ |
* Mask for grabbing the type out of the contents of data. |
1430 |
RandomAccessFile charFile = new RandomAccessFile(cFile, "r"); |
* @see CharData#DATA |
1431 |
unicodeData = new byte[(int) charFile.length()]; |
*/ |
1432 |
charFile.readFully(unicodeData, 0, unicodeData.length); |
private static final int TYPE_MASK = 0x1F; |
|
} |
|
|
catch (IOException e) |
|
|
{ |
|
|
throw new InternalError("Error reading Unicode attribute file: " |
|
|
+ e); |
|
|
} |
|
1433 |
|
|
1434 |
cachedCharAttr = new CharAttr('\0', false, CONTROL, -1, '\0', '\0', |
/** Mask for grabbing the non-breaking space flag out of the contents of |
1435 |
DIRECTIONALITY_BOUNDARY_NEUTRAL, false); |
* data. |
1436 |
} |
* @see CharData#DATA |
1437 |
|
*/ |
1438 |
|
private static final int NO_BREAK_MASK = 0x20; |
1439 |
|
|
1440 |
/** |
/** Mask for grabbing the mirrored directionality flag out of the contents |
1441 |
* Grabs a character out of the Unicode attribute database. See |
* of data. |
1442 |
* doc/unicode/unicode.database.format for details of the fields. |
* @see CharData#DATA |
|
* |
|
|
* @param ch the character to look up |
|
|
* @return the character's CharAttr |
|
1443 |
*/ |
*/ |
1444 |
private static CharAttr readChar(char ch) |
private static final int MIRROR_MASK = 0x40; |
|
{ |
|
|
CharAttr cached = cachedCharAttr; |
|
|
if (cached.ch == ch) |
|
|
return cached; |
|
|
|
|
|
Block b = getBlock(ch); |
|
|
if (b == null) |
|
|
return cachedCharAttr = new CharAttr(ch, false, UNASSIGNED, -1, ch, ch, |
|
|
DIRECTIONALITY_UNDEFINED, false); |
|
|
int offset = b.compressed ? b.offset |
|
|
: (b.offset + (ch - b.start) * ATTR_SIZE); |
|
|
byte flags = unicodeData[offset]; |
|
|
boolean noBreakSpace = (flags & 0x20) == 0x20; |
|
|
byte category = (byte) (flags & 0x1F); |
|
|
// numericValue, uppercase, and lowercase are signed in unicode.uni. |
|
|
int numericValue = (unicodeData[offset + 1] << 8) |
|
|
| (unicodeData[offset + 2] & 0xFF); |
|
|
char uppercase = (char) (ch - ((unicodeData[offset + 3] << 8) |
|
|
| (unicodeData[offset + 4] & 0xFF))); |
|
|
char lowercase = (char) (ch - ((unicodeData[offset + 5] << 8) |
|
|
| (unicodeData[offset + 6] & 0xFF))); |
|
|
if (b.compressed && numericValue >= 0) |
|
|
numericValue += ch - b.start; |
|
|
byte directionality = unicodeData[offset + 7]; |
|
|
boolean mirrored = (flags & 0x40) == 0x40; |
|
|
|
|
|
return cachedCharAttr = new CharAttr(ch, noBreakSpace, category, |
|
|
numericValue, uppercase, lowercase, |
|
|
directionality, mirrored); |
|
|
} |
|
1445 |
|
|
1446 |
/** |
/** |
1447 |
* Locates which block a character's information resides in. |
* Grabs an attribute offset from the Unicode attribute database. The lower |
1448 |
|
* 5 bits are the character type, the next 2 bits are flags, and the top |
1449 |
|
* 9 bits are the offset into the attribute tables. |
1450 |
* |
* |
1451 |
* @param ch the character to look up |
* @param ch the character to look up |
1452 |
* @return the block, or null |
* @return the character's attribute offset and type |
1453 |
|
* @see CharData#DATA |
1454 |
|
* @see CharData#SHIFT |
1455 |
*/ |
*/ |
1456 |
private static Block getBlock(char ch) |
private static char readChar(char ch) |
1457 |
{ |
{ |
1458 |
// Simple binary search. |
return data[blocks[ch >> CharData.SHIFT] |
1459 |
int low = 0; |
+ (ch & ~(-1 << CharData.SHIFT))]; |
|
int hi = blocks.length - 1; |
|
|
while (low <= hi) |
|
|
{ |
|
|
int mid = (low + hi) >> 1; |
|
|
Block b = blocks[mid]; |
|
|
if (ch < b.start) |
|
|
hi = mid - 1; |
|
|
else if (ch > b.end) |
|
|
low = mid + 1; |
|
|
else |
|
|
return b; |
|
|
} |
|
|
return null; |
|
1460 |
} |
} |
1461 |
|
|
1462 |
/** |
/** |
1510 |
*/ |
*/ |
1511 |
public String toString() |
public String toString() |
1512 |
{ |
{ |
1513 |
|
// This assumes that String.valueOf(char) can create a single-character |
1514 |
|
// String more efficiently than through the public API. |
1515 |
return String.valueOf(value); |
return String.valueOf(value); |
1516 |
} |
} |
1517 |
|
|
1524 |
*/ |
*/ |
1525 |
public String toString(char ch) |
public String toString(char ch) |
1526 |
{ |
{ |
1527 |
|
// This assumes that String.valueOf(char) can create a single-character |
1528 |
|
// String more efficiently than through the public API. |
1529 |
return String.valueOf(ch); |
return String.valueOf(ch); |
1530 |
} |
} |
1531 |
|
|
1617 |
*/ |
*/ |
1618 |
public static boolean isDefined(char ch) |
public static boolean isDefined(char ch) |
1619 |
{ |
{ |
1620 |
return getBlock(ch) != null; |
return getType(ch) == UNASSIGNED; |
1621 |
} |
} |
1622 |
|
|
1623 |
/** |
/** |
1640 |
*/ |
*/ |
1641 |
public static boolean isLetter(char ch) |
public static boolean isLetter(char ch) |
1642 |
{ |
{ |
1643 |
int category = getType(ch); |
return ((1 << getType(ch)) |
1644 |
return category >= UPPERCASE_LETTER && category <= OTHER_LETTER; |
& ((1 << UPPERCASE_LETTER) |
1645 |
|
| (1 << LOWERCASE_LETTER) |
1646 |
|
| (1 << TITLECASE_LETTER) |
1647 |
|
| (1 << MODIFIER_LETTER) |
1648 |
|
| (1 << OTHER_LETTER))) != 0; |
1649 |
} |
} |
1650 |
|
|
1651 |
/** |
/** |
1665 |
*/ |
*/ |
1666 |
public static boolean isLetterOrDigit(char ch) |
public static boolean isLetterOrDigit(char ch) |
1667 |
{ |
{ |
1668 |
int category = getType(ch); |
return ((1 << getType(ch)) |
1669 |
return (category <= OTHER_LETTER && category >= UPPERCASE_LETTER) |
& ((1 << UPPERCASE_LETTER) |
1670 |
|| category == DECIMAL_DIGIT_NUMBER; |
| (1 << LOWERCASE_LETTER) |
1671 |
|
| (1 << TITLECASE_LETTER) |
1672 |
|
| (1 << MODIFIER_LETTER) |
1673 |
|
| (1 << OTHER_LETTER) |
1674 |
|
| (1 << DECIMAL_DIGIT_NUMBER))) != 0; |
1675 |
} |
} |
1676 |
|
|
1677 |
/** |
/** |
1735 |
*/ |
*/ |
1736 |
public static boolean isJavaIdentifierStart(char ch) |
public static boolean isJavaIdentifierStart(char ch) |
1737 |
{ |
{ |
1738 |
int category = getType(ch); |
return ((1 << getType(ch)) |
1739 |
return (category <= OTHER_LETTER && category >= UPPERCASE_LETTER) |
& ((1 << UPPERCASE_LETTER) |
1740 |
|| category == LETTER_NUMBER || category == CURRENCY_SYMBOL |
| (1 << LOWERCASE_LETTER) |
1741 |
|| category == CONNECTOR_PUNCTUATION; |
| (1 << TITLECASE_LETTER) |
1742 |
|
| (1 << MODIFIER_LETTER) |
1743 |
|
| (1 << OTHER_LETTER) |
1744 |
|
| (1 << LETTER_NUMBER) |
1745 |
|
| (1 << CURRENCY_SYMBOL) |
1746 |
|
| (1 << CONNECTOR_PUNCTUATION))) != 0; |
1747 |
} |
} |
1748 |
|
|
1749 |
/** |
/** |
1768 |
public static boolean isJavaIdentifierPart(char ch) |
public static boolean isJavaIdentifierPart(char ch) |
1769 |
{ |
{ |
1770 |
int category = getType(ch); |
int category = getType(ch); |
1771 |
if (category == CONTROL) |
return ((1 << category) |
1772 |
return isIdentifierIgnorable(ch); |
& ((1 << UPPERCASE_LETTER) |
1773 |
return (category <= NON_SPACING_MARK && category >= UPPERCASE_LETTER) |
| (1 << LOWERCASE_LETTER) |
1774 |
|| (category <= LETTER_NUMBER && category >= COMBINING_SPACING_MARK) |
| (1 << TITLECASE_LETTER) |
1775 |
|| category == CURRENCY_SYMBOL || category == CONNECTOR_PUNCTUATION |
| (1 << MODIFIER_LETTER) |
1776 |
|| category == FORMAT; |
| (1 << OTHER_LETTER) |
1777 |
|
| (1 << NON_SPACING_MARK) |
1778 |
|
| (1 << COMBINING_SPACING_MARK) |
1779 |
|
| (1 << DECIMAL_DIGIT_NUMBER) |
1780 |
|
| (1 << LETTER_NUMBER) |
1781 |
|
| (1 << CURRENCY_SYMBOL) |
1782 |
|
| (1 << CONNECTOR_PUNCTUATION) |
1783 |
|
| (1 << FORMAT))) != 0 |
1784 |
|
|| (category == CONTROL && isIdentifierIgnorable(ch)); |
1785 |
} |
} |
1786 |
|
|
1787 |
/** |
/** |
1800 |
*/ |
*/ |
1801 |
public static boolean isUnicodeIdentifierStart(char ch) |
public static boolean isUnicodeIdentifierStart(char ch) |
1802 |
{ |
{ |
1803 |
int category = getType(ch); |
return ((1 << getType(ch)) |
1804 |
return (category <= OTHER_LETTER && category >= UPPERCASE_LETTER) |
& ((1 << UPPERCASE_LETTER) |
1805 |
|| category == LETTER_NUMBER; |
| (1 << LOWERCASE_LETTER) |
1806 |
|
| (1 << TITLECASE_LETTER) |
1807 |
|
| (1 << MODIFIER_LETTER) |
1808 |
|
| (1 << OTHER_LETTER) |
1809 |
|
| (1 << LETTER_NUMBER))) != 0; |
1810 |
} |
} |
1811 |
|
|
1812 |
/** |
/** |
1830 |
public static boolean isUnicodeIdentifierPart(char ch) |
public static boolean isUnicodeIdentifierPart(char ch) |
1831 |
{ |
{ |
1832 |
int category = getType(ch); |
int category = getType(ch); |
1833 |
if (category == CONTROL) |
return ((1 << category) |
1834 |
return isIdentifierIgnorable(ch); |
& ((1 << UPPERCASE_LETTER) |
1835 |
return (category <= NON_SPACING_MARK && category >= UPPERCASE_LETTER) |
| (1 << LOWERCASE_LETTER) |
1836 |
|| (category <= LETTER_NUMBER && category >= COMBINING_SPACING_MARK) |
| (1 << TITLECASE_LETTER) |
1837 |
|| category == CONNECTOR_PUNCTUATION || category == FORMAT; |
| (1 << MODIFIER_LETTER) |
1838 |
|
| (1 << OTHER_LETTER) |
1839 |
|
| (1 << NON_SPACING_MARK) |
1840 |
|
| (1 << COMBINING_SPACING_MARK) |
1841 |
|
| (1 << DECIMAL_DIGIT_NUMBER) |
1842 |
|
| (1 << LETTER_NUMBER) |
1843 |
|
| (1 << CONNECTOR_PUNCTUATION) |
1844 |
|
| (1 << FORMAT))) != 0 |
1845 |
|
|| (category == CONTROL && isIdentifierIgnorable(ch)); |
1846 |
} |
} |
1847 |
|
|
1848 |
/** |
/** |
1863 |
*/ |
*/ |
1864 |
public static boolean isIdentifierIgnorable(char ch) |
public static boolean isIdentifierIgnorable(char ch) |
1865 |
{ |
{ |
1866 |
return ch <= '\u0008' || (ch <= '\u001B' && ch >= '\u000E') |
return (ch <= '\u009F' && (ch < '\t' || ch >= '\u007F' |
1867 |
|| (ch <= '\u009F' && ch >= '\u007F') || getType(ch) == FORMAT; |
|| (ch <= '\u001B' && ch >= '\u000E'))) |
1868 |
|
|| getType(ch) == FORMAT; |
1869 |
} |
} |
1870 |
|
|
1871 |
/** |
/** |
1883 |
*/ |
*/ |
1884 |
public static char toLowerCase(char ch) |
public static char toLowerCase(char ch) |
1885 |
{ |
{ |
1886 |
return readChar(ch).lowercase; |
// Signedness doesn't matter, as result is cast back to char. |
1887 |
|
return (char) (ch + lower[readChar(ch) >> 7]); |
1888 |
} |
} |
1889 |
|
|
1890 |
/** |
/** |
1902 |
*/ |
*/ |
1903 |
public static char toUpperCase(char ch) |
public static char toUpperCase(char ch) |
1904 |
{ |
{ |
1905 |
return readChar(ch).uppercase; |
// Signedness doesn't matter, as result is cast back to char. |
1906 |
|
return (char) (ch + upper[readChar(ch) >> 7]); |
1907 |
} |
} |
1908 |
|
|
1909 |
/** |
/** |
1920 |
*/ |
*/ |
1921 |
public static char toTitleCase(char ch) |
public static char toTitleCase(char ch) |
1922 |
{ |
{ |
1923 |
for (int i = 0; i < tcs.length; i++) |
// As title is short, it doesn't hurt to exhaustively iterate over it. |
1924 |
if (tcs[i][0] == ch) |
for (int i = title.length - 2; i >= 0; i -= 2) |
1925 |
return tcs[i][1]; |
if (title[i] == ch) |
1926 |
|
return title[i + 1]; |
1927 |
return toUpperCase(ch); |
return toUpperCase(ch); |
1928 |
} |
} |
1929 |
|
|
1949 |
{ |
{ |
1950 |
if (radix < MIN_RADIX || radix > MAX_RADIX) |
if (radix < MIN_RADIX || radix > MAX_RADIX) |
1951 |
return -1; |
return -1; |
1952 |
CharAttr attr = readChar(ch); |
char attr = readChar(ch); |
1953 |
int category = attr.category; |
if (((1 << (attr & TYPE_MASK)) |
1954 |
if (category == DECIMAL_DIGIT_NUMBER || category == LOWERCASE_LETTER |
& ((1 << UPPERCASE_LETTER) |
1955 |
|| category == UPPERCASE_LETTER) |
| (1 << LOWERCASE_LETTER) |
1956 |
|
| (1 << DECIMAL_DIGIT_NUMBER))) != 0) |
1957 |
{ |
{ |
1958 |
int digit = attr.numericValue; |
// Signedness doesn't matter; 0xffff vs. -1 are both rejected. |
1959 |
|
int digit = numValue[attr >> 7]; |
1960 |
return (digit >= 0 && digit < radix) ? digit : -1; |
return (digit >= 0 && digit < radix) ? digit : -1; |
1961 |
} |
} |
1962 |
return -1; |
return -1; |
1991 |
*/ |
*/ |
1992 |
public static int getNumericValue(char ch) |
public static int getNumericValue(char ch) |
1993 |
{ |
{ |
1994 |
return readChar(ch).numericValue; |
// Treat numValue as signed. |
1995 |
|
return (short) numValue[readChar(ch) >> 7]; |
1996 |
} |
} |
1997 |
|
|
1998 |
/** |
/** |
2010 |
*/ |
*/ |
2011 |
public static boolean isSpace(char ch) |
public static boolean isSpace(char ch) |
2012 |
{ |
{ |
2013 |
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f'; |
// Performing the subtraction up front alleviates need to compare longs. |
2014 |
|
return ch-- <= ' ' && ((1 << ch) |
2015 |
|
& ((1 << (' ' - 1)) |
2016 |
|
| (1 << ('\t' - 1)) |
2017 |
|
| (1 << ('\n' - 1)) |
2018 |
|
| (1 << ('\r' - 1)) |
2019 |
|
| (1 << ('\f' - 1)))) != 0; |
2020 |
} |
} |
2021 |
|
|
2022 |
/** |
/** |
2032 |
*/ |
*/ |
2033 |
public static boolean isSpaceChar(char ch) |
public static boolean isSpaceChar(char ch) |
2034 |
{ |
{ |
2035 |
int category = readChar(ch).category; |
return ((1 << getType(ch)) |
2036 |
return category >= SPACE_SEPARATOR && category <= PARAGRAPH_SEPARATOR; |
& ((1 << SPACE_SEPARATOR) |
2037 |
|
| (1 << LINE_SEPARATOR) |
2038 |
|
| (1 << PARAGRAPH_SEPARATOR))) != 0; |
2039 |
} |
} |
2040 |
|
|
2041 |
/** |
/** |
2057 |
*/ |
*/ |
2058 |
public static boolean isWhitespace(char ch) |
public static boolean isWhitespace(char ch) |
2059 |
{ |
{ |
2060 |
if ((ch <= '\r' && ch >= '\u0009') || (ch <= '\u001F' && ch >= '\u001C')) |
int attr = readChar(ch); |
2061 |
return true; |
return ((((1 << (attr & TYPE_MASK)) |
2062 |
CharAttr attr = readChar(ch); |
& ((1 << SPACE_SEPARATOR) |
2063 |
int category = attr.category; |
| (1 << LINE_SEPARATOR) |
2064 |
return (category == SPACE_SEPARATOR && ! attr.noBreakSpace) |
| (1 << PARAGRAPH_SEPARATOR))) != 0) |
2065 |
|| category == LINE_SEPARATOR || category == PARAGRAPH_SEPARATOR; |
&& (attr & NO_BREAK_MASK) == 0) |
2066 |
|
|| (ch <= '\u001F' && ((1 << ch) |
2067 |
|
& ((1 << '\t') |
2068 |
|
| (1 << '\n') |
2069 |
|
| (1 << '\u000B') |
2070 |
|
| (1 << '\u000C') |
2071 |
|
| (1 << '\r') |
2072 |
|
| (1 << '\u001C') |
2073 |
|
| (1 << '\u001D') |
2074 |
|
| (1 << '\u001E') |
2075 |
|
| (1 << '\u001F'))) != 0); |
2076 |
} |
} |
2077 |
|
|
2078 |
/** |
/** |
2130 |
*/ |
*/ |
2131 |
public static int getType(char ch) |
public static int getType(char ch) |
2132 |
{ |
{ |
2133 |
return readChar(ch).category; |
return readChar(ch) & TYPE_MASK; |
2134 |
} |
} |
2135 |
|
|
2136 |
/** |
/** |
2186 |
*/ |
*/ |
2187 |
public static byte getDirectionality(char ch) |
public static byte getDirectionality(char ch) |
2188 |
{ |
{ |
2189 |
return readChar(ch).directionality; |
// The result will correctly be signed. |
2190 |
|
return (byte) direction[readChar(ch) >> 7]; |
2191 |
} |
} |
2192 |
|
|
2193 |
/** |
/** |
2201 |
*/ |
*/ |
2202 |
public static boolean isMirrored(char ch) |
public static boolean isMirrored(char ch) |
2203 |
{ |
{ |
2204 |
return readChar(ch).mirrored; |
return (readChar(ch) & MIRROR_MASK) != 0; |
2205 |
} |
} |
2206 |
|
|
2207 |
/** |
/** |
2235 |
{ |
{ |
2236 |
return compareTo((Character) o); |
return compareTo((Character) o); |
2237 |
} |
} |
|
|
|
|
/** |
|
|
* Represents an entry in gnu/java/locale/block.uni. |
|
|
*/ |
|
|
private static final class Block |
|
|
{ |
|
|
/** The start character of a compressed block. */ |
|
|
final char start; |
|
|
/** The end character of a compressed block. */ |
|
|
final char end; |
|
|
/** True if the block represents multiple characters. */ |
|
|
final boolean compressed; |
|
|
/** The offset of the block. */ |
|
|
final int offset; |
|
|
|
|
|
/** |
|
|
* Construct a block. |
|
|
* @param start the start character |
|
|
* @param end the end character |
|
|
* @param offset the offset and compression of the block: the most |
|
|
* significant bit is the compression, the remainder are the offset |
|
|
*/ |
|
|
Block(char start, char end, short offset) |
|
|
{ |
|
|
this.start = start; |
|
|
this.end = end; |
|
|
this.compressed = offset < 0; |
|
|
this.offset = offset & Short.MAX_VALUE; |
|
|
} |
|
|
} // class Block |
|
|
|
|
|
/** |
|
|
* Represents all the attributes stored for a character. |
|
|
*/ |
|
|
private static final class CharAttr |
|
|
{ |
|
|
/** The character of this attribute. */ |
|
|
final char ch; |
|
|
/** If the character is a non-breaking space. */ |
|
|
final boolean noBreakSpace; |
|
|
/** The character category. */ |
|
|
final int category; |
|
|
/** The character value. */ |
|
|
final int numericValue; |
|
|
/** The uppercase version of the character. */ |
|
|
final char uppercase; |
|
|
/** The lowercase version of the character. */ |
|
|
final char lowercase; |
|
|
/** The directionality of the character. */ |
|
|
final byte directionality; |
|
|
/** Whether the character is mirrored. */ |
|
|
final boolean mirrored; |
|
|
|
|
|
/** |
|
|
* Construct the attributes for a character. |
|
|
* @param ch the character |
|
|
* @param noBreakSpace if it is a non-breaking space |
|
|
* @param category its category |
|
|
* @param numericValue its value, or -1 (none), -2 (not representable) |
|
|
* @param uppercase the uppercase version, or '\0' |
|
|
* @param lowercase the lowercase version, or '\0' |
|
|
* @param directionality the directionality |
|
|
* @param mirrored if it is mirrored |
|
|
*/ |
|
|
CharAttr(char ch, boolean noBreakSpace, byte category, int numericValue, |
|
|
char uppercase, char lowercase, byte directionality, |
|
|
boolean mirrored) |
|
|
{ |
|
|
this.ch = ch; |
|
|
this.noBreakSpace = noBreakSpace; |
|
|
this.category = category; |
|
|
this.numericValue = numericValue; |
|
|
this.uppercase = uppercase == '\0' ? ch : uppercase; |
|
|
this.lowercase = lowercase == '\0' ? ch : lowercase; |
|
|
this.directionality = directionality; |
|
|
this.mirrored = mirrored; |
|
|
} |
|
|
} // class CharAttr |
|
2238 |
} //class Character |
} //class Character |