undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @summary The characters FFFE and FFFF should not be in a UnicodeBlock. They
* should be in a null block.
* @bug 4404588
* @author John O'Conner
*/
public class Bug4404588 {
public Bug4404588() {
// do nothing
}
public static void main(String[] args) {
Bug4404588 test = new Bug4404588();
test.run();
}
/**
* Test the correct data against what Character reports.
*/
void run() {
Character ch;
Character.UnicodeBlock block;
for(int x=0; x < charData.length; x++) {
ch = (Character)charData[x][0];
block = (Character.UnicodeBlock)charData[x][1];
if (Character.UnicodeBlock.of(ch.charValue()) != block) {
System.err.println("Error: block = " + block);
System.err.println("Character.UnicodeBlock.of(" +
Integer.toHexString(ch.charValue()) +") = " +
Character.UnicodeBlock.of(ch.charValue()));
throw new RuntimeException("Blocks aren't equal.");
}
}
System.out.println("Passed.");
}
/**
* Contains the character data to test. The first object is the character.
* The next object is the UnicodeBlock to which it should belong.
*/
Object[][] charData = {
{ new Character('\uFFFE'), Character.UnicodeBlock.SPECIALS },
{ new Character('\uFFFF'), Character.UnicodeBlock.SPECIALS },
};
}

View file

@ -0,0 +1,558 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
*
* @author Alan Liu
* @author John O'Conner
*/
import java.io.*;
/**
* This class either loads or dumps the character properties of all Unicode
* characters out to a file. When loading, it compares the loaded data with
* that obtained through the java.lang.Character API. This allows detection of
* changes to the character properties between versions of the Java VM. A
* typical usage would be to dump the properties under an early VM, and load
* them under a later VM.
*
* Also: Check the current VM's character properties against those in a
* Unicode database. The database should be of the format
* available on ftp.unicode.org/Public/UNIDATA.
*
*/
public class CharCheck {
static int differences = 0;
public static void main(String args[]) throws Exception {
if (args.length != 2 && args.length != 3) usage();
if (args[0].equals("dump"))
dump(Integer.parseInt(args[1], 16), new ObjectOutputStream(new FileOutputStream(args[2])));
else if (args[0].equals("load"))
load(Integer.parseInt(args[1], 16), new ObjectInputStream(new FileInputStream(args[2])));
else if (args[0].equals("check"))
check(Integer.parseInt(args[1], 16), new File(args[2]));
else if (args[0].equals("char"))
showChar(Integer.parseInt(args[1],16));
else if (args[0].equals("fchar"))
showFileChar(args[1], Integer.parseInt(args[2],16));
else usage();
if (differences != 0) {
throw new RuntimeException("There are differences between Character properties and the specification.");
}
}
static void usage() {
System.err.println("Usage: java CharCheck <command>");
System.err.println("where <command> is one of the following:");
System.err.println("dump <plane> <file> - dumps the character properties of the given plane,");
System.err.println(" read from the current VM, to the given file.");
System.err.println("load <plane> <file> - loads the character properties from the given");
System.err.println(" file and compares them to those of the given character plane");
System.err.println(" in the current VM.");
System.err.println("check <plane> <file> - compare the current VM's character properties");
System.err.println(" in the given plane to those listed in the given file, ");
System.err.println(" which should be in the format available on ");
System.err.println(" ftp.unicode.org/Public/2.0-Update.");
System.err.println("char <code> - show current VM properties of the given Unicode char.");
System.err.println("fchar <file> <code> - show file properties of the given Unicode char.");
System.exit(0);
}
static String getTypeName(int type) {
return (type >= 0 && type < UnicodeSpec.generalCategoryList.length) ?
(UnicodeSpec.generalCategoryList[type][UnicodeSpec.LONG] + '(' + type + ')') :
("<Illegal type value " + type + ">");
}
static int check(int plane, File specFile) throws Exception {
String version = System.getProperty("java.version");
System.out.println("Current VM version " + version);
int rangeLimit = (plane << 16) | 0xFFFF;
String record;
UnicodeSpec[] spec = UnicodeSpec.readSpecFile(specFile, plane);
int rangeStart = 0x0000;
boolean isRange = false;
lastCheck = (plane << 16) - 1;
for (int currentSpec = 0; currentSpec < spec.length; currentSpec++) {
int c = spec[currentSpec].getCodePoint();
if (isRange) {
// Must see end of range now
if (spec[currentSpec].getName().endsWith("Last>")) {
for (int d=rangeStart; d<=c; d++) {
checkOneChar(d, spec[currentSpec]);
}
}
else {
// No good -- First without Last
System.out.println("BAD FILE: First without last at '" + escape(rangeStart) + "'");
}
isRange = false;
}
else {
// Look for a First, Last pair: This is a pair of entries like the following:
// 4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;;
// 9FA5;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
if (spec[currentSpec].getName().endsWith("First>")) {
rangeStart = c;
isRange = true;
}
else {
checkOneChar(c, spec[currentSpec]);
}
}
}
// Check undefined chars at the end of the range
while (lastCheck < rangeLimit) checkOneCharDefined(++lastCheck, "?", false);
System.out.println("Total differences: "+differences);
return differences;
}
static int lastCheck = -1;
static final void checkOneCharDefined(int c, String name, boolean fileDefined) {
if (Character.isDefined(c) != fileDefined)
showDifference(c, name, "isDefined", ""+(!fileDefined), ""+fileDefined);
}
// In GenerateCharacter, the following ranges are handled specially.
// Each is the start of a 26-character range with values 10..35.
static final char NUMERIC_EXCEPTION[] = { '\u0041', '\u0061', '\uFF21', '\uFF41' };
static void checkOneChar(int c, UnicodeSpec charSpec) {
// Handle intervening ranges -- we assume that we will be called in monotonically
// increasing order. If the last char we checked is more than one before this
// char, then check the intervening range -- it should all be undefined.
int lowerLimit = (c & 0xFF0000);
if (lastCheck >= lowerLimit && (lastCheck+1) != c) {
for (int i=lastCheck+1; i<c; ++i)
checkOneCharDefined(i, "?", false);
}
lastCheck = c;
// isDefined should be true
checkOneCharDefined(c, charSpec.getName(), true);
// Check lower, upper, and titlecase conversion
int upper = Character.toUpperCase(c);
int lower = Character.toLowerCase(c);
int title = Character.toTitleCase(c);
int upperDB = charSpec.hasUpperMap() ? charSpec.getUpperMap() : c;
int lowerDB = charSpec.hasLowerMap() ? charSpec.getLowerMap() : c;
int titleDB = charSpec.hasTitleMap() ? charSpec.getTitleMap() : c;
if (upper != upperDB) showDifference(c, charSpec.getName(), "upper", hex6(upper), hex6(upperDB));
if (lower != lowerDB) showDifference(c, charSpec.getName(), "lower", hex6(lower), hex6(lowerDB));
if (title != titleDB) showDifference(c, charSpec.getName(), "title", hex6(title), hex6(titleDB));
// Check the character general category (type)
int type = Character.getType(c);
int typeDB = charSpec.getGeneralCategory();
if (type != typeDB) {
showDifference(c, charSpec.getName(), "type",
UnicodeSpec.generalCategoryList[type][UnicodeSpec.SHORT],
UnicodeSpec.generalCategoryList[typeDB][UnicodeSpec.SHORT]);
}
// Check the mirrored property
boolean isMirrored = Character.isMirrored(c);
boolean isMirroredDB = charSpec.isMirrored();
if (isMirrored != isMirroredDB) {
showDifference(c, charSpec.getName(), "isMirrored", ""+isMirrored, ""+isMirroredDB);
}
// Check the directionality property
byte directionality = Character.getDirectionality(c);
byte directionalityDB = charSpec.getBidiCategory();
if (directionality != directionalityDB) {
showDifference(c, charSpec.getName(), "directionality", ""+directionality, ""+directionalityDB);
}
// Check the decimal digit property
int decimalDigit = Character.digit(c, 10);
int decimalDigitDB = -1;
if (charSpec.getGeneralCategory() == UnicodeSpec.DECIMAL_DIGIT_NUMBER) {
decimalDigitDB = charSpec.getDecimalValue();
}
if (decimalDigit != decimalDigitDB)
showDifference(c, charSpec.getName(), "decimal digit", ""+decimalDigit, ""+decimalDigitDB);
// Check the numeric property
int numericValue = Character.getNumericValue(c);
int numericValueDB;
if (charSpec.getNumericValue().length() == 0) {
numericValueDB = -1;
// Handle exceptions where Character deviates from the UCS spec
for (int k=0; k<NUMERIC_EXCEPTION.length; ++k) {
if (c >= NUMERIC_EXCEPTION[k] && c < (char)(NUMERIC_EXCEPTION[k]+26)) {
numericValueDB = c - NUMERIC_EXCEPTION[k] + 10;
break;
}
}
}
else {
String strValue = charSpec.getNumericValue();
int parsedNumericValue;
if (strValue.equals("10000000000")
|| strValue.equals("1000000000000")) {
System.out.println("Skipping strValue: " + strValue
+ " for " + charSpec.getName()
+ "(0x" + Integer.toHexString(c) + ")");
parsedNumericValue = -2;
} else {
parsedNumericValue = strValue.indexOf('/') < 0 ?
Integer.parseInt(strValue) : -2;
}
numericValueDB = parsedNumericValue < 0 ? -2 : parsedNumericValue;
}
if (numericValue != numericValueDB)
showDifference(c, charSpec.getName(), "numeric value", ""+numericValue, ""+numericValueDB);
}
static void showDifference(int c, String name, String property, String vmValue, String dbValue) {
System.out.println(escape("Mismatch at '" + hex6(c) + "' (" + name+ "): " +
property + "=" + vmValue + ", db=" + dbValue));
++differences;
}
/**
* Given a record containing ';'-separated fields, return the fieldno-th
* field. The first field is field 0.
*/
static String getField(String record, int fieldno) {
int i=0;
int j=record.indexOf(';');
while (fieldno > 0) {
i=j+1;
j=record.indexOf(';', i);
}
return record.substring(i, j);
}
static final int FIELD_COUNT = 15;
/**
* Given a record containing ';'-separated fields, return an array of
* the fields. It is assumed that there are FIELD_COUNT fields per record.
*/
static void getFields(String record, String[] fields) {
int i=0;
int j=record.indexOf(';');
fields[0] = record.substring(i, j);
for (int n=1; n<FIELD_COUNT; ++n) {
i=j+1;
j=record.indexOf(';', i);
fields[n] = (j<0) ? record.substring(i) : record.substring(i, j);
}
}
/**
* Given a record containing ';'-separated fields, return an array of
* the fields. It is assumed that there are FIELD_COUNT fields per record.
*/
static String[] getFields(String record) {
String[] fields = new String[FIELD_COUNT];
getFields(record, fields);
return fields;
}
static void dump(int plane, ObjectOutputStream out) throws Exception {
String version = System.getProperty("java.version");
System.out.println("Writing file version " + version);
out.writeObject(version);
long[] data = new long[0x20000];
long[] onechar = new long[2];
int j=0;
int begin = plane<<16;
int end = begin + 0xFFFF;
for (int i = begin; i <= end; ++i) {
getPackedCharacterData(i, onechar);
data[j++] = onechar[0];
data[j++] = onechar[1];
}
out.writeObject(data);
}
static long[] loadData(ObjectInputStream in) throws Exception {
String version = System.getProperty("java.version");
String inVersion = (String)in.readObject();
System.out.println("Reading file version " + inVersion);
System.out.println("Current version " + version);
long[] data = (long[])in.readObject();
if (data.length != 0x20000) {
System.out.println("BAD ARRAY LENGTH: " + data.length);
}
return data;
}
static int load(int plane, ObjectInputStream in) throws Exception {
long[] data = CharCheck.loadData(in);
CharCheck.checkData(data, plane);
return differences;
}
static int checkData(long[] data, int plane) {
long[] onechar = new long[2];
for (int i=0; i<0x10000; ++i) {
int c = (plane << 16) | i;
getPackedCharacterData(c, onechar);
if (data[2*i] != onechar[0] || data[2*i+1] != onechar[1]) {
long[] filechar = { data[2*i], data[2*i+1] };
showDifference(c, onechar, filechar);
}
}
System.out.println("Total differences: " + differences);
return differences;
}
static String hex6(long n) {
String q = Long.toHexString(n).toUpperCase();
return "000000".substring(Math.min(6, q.length())) + q;
}
static void showChar(int c) {
long[] chardata = new long[2];
getPackedCharacterData(c, chardata);
System.out.println("Current VM properties for '" + hex6(c) + "': " +
hex6(chardata[1]) + ' ' + hex6(chardata[0]));
String[] data = unpackCharacterData(chardata);
for (int i=0; i<data.length; ++i)
System.out.println(" " + escape(data[i]));
}
static void showFileChar(String fileName, int c) throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName));
String inVersion = (String)in.readObject();
System.out.println("Reading file version " + inVersion);
long[] data = (long[])in.readObject();
if (data.length != 0x20000) {
System.out.println("BAD ARRAY LENGTH: " + data.length);
}
int offset = c & 0xFFFF;
long[] chardata = { data[2*offset], data[2*offset+1] };
String[] datap = unpackCharacterData(chardata);
System.out.println(escape("File properties for '" + hex6(c)+ "':"));
for (int i=0; i<datap.length; ++i)
System.out.println(" " + escape(datap[i]));
}
/**
* The packed character data encapsulates all the information obtainable
* about a character in a single numeric value.
*
* data[0]:
*
* 5 bits for getType()
* 6 bits for digit() -- add one
* 6 bits for getNumericValue() -- add two
* 15 bits for isXxx()
*
* 21 bits for toUpperCase()
*
*
* data[1]:
* 21 bits for toLowerCase()
* 21 bits for toTitleCase()
*/
static void getPackedCharacterData(int c, long[] data) {
data[0] =
(long)Character.getType(c) |
((long)(Character.digit(c, Character.MAX_RADIX) + 1) << 5) |
((long)(Character.getNumericValue(c) + 2) << 11) |
(Character.isDefined(c) ? (1L<<17) : 0L) |
(Character.isDigit(c) ? (1L<<18) : 0L) |
(Character.isIdentifierIgnorable(c) ? (1L<<19) : 0L) |
(Character.isISOControl(c) ? (1L<<20) : 0L) |
(Character.isJavaIdentifierPart(c) ? (1L<<21) : 0L) |
(Character.isJavaIdentifierStart(c) ? (1L<<22) : 0L) |
(Character.isLetter(c) ? (1L<<23) : 0L) |
(Character.isLetterOrDigit(c) ? (1L<<24) : 0L) |
(Character.isLowerCase(c) ? (1L<<25) : 0L) |
(Character.isSpaceChar(c) ? (1L<<26) : 0L) |
(Character.isTitleCase(c) ? (1L<<27) : 0L) |
(Character.isUnicodeIdentifierPart(c) ? (1L<<28) : 0L) |
(Character.isUnicodeIdentifierStart(c) ? (1L<<29) : 0L) |
(Character.isUpperCase(c) ? (1L<<30) : 0L) |
(Character.isWhitespace(c) ? (1L<<31) : 0L) |
((long)Character.toUpperCase(c) << 32);
data[1] = (long)Character.toLowerCase(c) |
((long)Character.toTitleCase(c) << 21);
}
/**
* Given a long, set the bits at the given offset and length to the given value.
*/
static long setBits(long data, int offset, int length, long value) {
long himask = -1L << (offset+length);
long lomask = ~(-1L << offset);
long lengthmask = ~(-1L << length);
return (data & (himask | lomask)) | ((value & lengthmask) << offset);
}
/**
* Given packed character data, change the attribute
* toLower
*/
static void setToLower(long[] data, int value) {
data[0] = setBits(data[0], 48, 16, value);
}
/**
* Given packed character data, change the attribute
* toUpper
*/
static void setToUpper(long[] data, int value) {
data[0] = setBits(data[0], 32, 16, value);
}
/**
* Given packed character data, change the attribute
* toTitle
*/
static void setToTitle(long[] data, int value) {
data[1] = value;
}
/**
* Given packed character data, change the attribute
* getType
*/
static void setGetType(long[] data, int value) {
data[0] = setBits(data[0], 0, 5, value);
}
/**
* Given packed character data, change the attribute
* isDefined
*/
static void setIsDefined(long[] data, boolean value) {
data[0] = setBits(data[0], 17, 1, value?1:0);
}
/**
* Given packed character data, change the attribute
* isJavaIdentifierPart
*/
static void setIsJavaIdentifierPart(long[] data, boolean value) {
data[0] = setBits(data[0], 21, 1, value?1:0);
}
/**
* Given packed character data, change the attribute
* isJavaIdentifierStart
*/
static void setIsJavaIdentifierStart(long[] data, boolean value) {
data[0] = setBits(data[0], 22, 1, value?1:0);
}
static String[] unpackCharacterData(long[] dataL) {
long data = dataL[0];
String[] result = {
"type=" + getTypeName((int)(data&0x1F)),
"digit=" + (((data>>5)&0x3F)-1),
"numeric=" + (((data>>11)&0x3F)-2),
"isDefined=" + (((data>>17)&1)==1),
"isDigit=" + (((data>>18)&1)==1),
"isIdentifierIgnorable=" + (((data>>19)&1)==1),
"isISOControl=" + (((data>>20)&1)==1),
"isJavaIdentifierPart=" + (((data>>21)&1)==1),
"isJavaIdentifierStart=" + (((data>>22)&1)==1),
"isLetter=" + (((data>>23)&1)==1),
"isLetterOrDigit=" + (((data>>24)&1)==1),
"isLowerCase=" + (((data>>25)&1)==1),
"isSpaceChar=" + (((data>>26)&1)==1),
"isTitleCase=" + (((data>>27)&1)==1),
"isUnicodeIdentifierPart=" + (((data>>28)&1)==1),
"isUnicodeIdentifierStart=" + (((data>>29)&1)==1),
"isUpperCase=" + (((data>>30)&1)==1),
"isWhitespace=" + (((data>>31)&1)==1),
"toUpper=" + hex6(((int)(data>>32) & 0X1FFFFF)),
"toLower=" + hex6((int)(dataL[1] & 0x1FFFFF)),
"toTitle=" + hex6(((int)(dataL[1] >> 21) & 0x1FFFFF))
};
return result;
}
static String[] getCharacterData(int c) {
long[] data = new long[2];
getPackedCharacterData(c, data);
return unpackCharacterData(data);
}
static void showDifference(int c, long[] currentData, long[] fileData) {
System.out.println("Difference at " + hex6(c));
String[] current = unpackCharacterData(currentData);
String[] file = unpackCharacterData(fileData);
for (int i=0; i<current.length; ++i) {
if (!current[i].equals(file[i])) {
System.out.println(escape(" current " + current[i] +
", file " + file[i]));
}
}
++differences;
}
static String escape(String s) {
StringBuffer buf = new StringBuffer();
for (int i=0; i<s.length(); ++i) {
char c = s.charAt(i);
if (c >= 0x20 && c <= 0x7F) buf.append(c);
else {
buf.append("\\u");
String h = "000" + Integer.toHexString(c);
if (h.length() > 4) h = h.substring(h.length() - 4);
buf.append(h);
}
}
return buf.toString();
}
static String escape(int c) {
StringBuffer buf = new StringBuffer();
if (c >= 0x20 && c <= 0x7F) buf.append(c);
else {
buf.append("\\u");
String h = "000" + Integer.toHexString(c);
if (h.length() > 4) h = h.substring(h.length() - 4);
buf.append(h);
}
return buf.toString();
}
}
//eof

View file

@ -0,0 +1,308 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8202771 8221431 8229831 8296246
* @summary Check j.l.Character.isDigit/isLetter/isLetterOrDigit/isSpaceChar
* /isWhitespace/isTitleCase/isISOControl/isIdentifierIgnorable
* /isJavaIdentifierStart/isJavaIdentifierPart/isUnicodeIdentifierStart
* /isUnicodeIdentifierPart
* @library /lib/testlibrary/java/lang
* @run main CharPropTest
*/
import java.nio.file.Files;
import java.util.stream.Stream;
public class CharPropTest {
private static int diffs = 0;
private static int rangeStart = 0x0000;
private static boolean isRange = false;
public static void main(String[] args) throws Exception {
try (Stream<String> lines = Files.lines(UCDFiles.UNICODE_DATA)) {
lines.map(String::trim)
.filter(line -> line.length() != 0 && line.charAt(0) != '#')
.forEach(line -> handleOneLine(line));
if (diffs != 0) {
throw new RuntimeException("Total differences: " + diffs);
}
}
}
private static void handleOneLine(String line) {
String[] fields = line.split(";");
int currentCp = Integer.parseInt(fields[0], 16);
String name = fields[1];
String category = fields[2];
// Except single code point, also handle ranges like the following:
// 3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
// 4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;
if (isRange) {
if (name.endsWith("Last>")) {
for (int cp = rangeStart; cp <= currentCp; cp++) {
testCodePoint(cp, category);
}
} else {
throw new RuntimeException("Not a valid range, first range <"
+ Integer.toHexString(rangeStart) + "> without last.");
}
isRange = false;
} else {
if (name.endsWith("First>")) {
rangeStart = currentCp;
isRange = true;
} else {
testCodePoint(currentCp, category);
}
}
}
private static void testCodePoint(int codePoint, String category) {
isDigitTest(codePoint, category);
isLetterTest(codePoint, category);
isLetterOrDigitTest(codePoint, category);
isSpaceCharTest(codePoint, category);
isWhitespaceTest(codePoint, category);
isTitleCaseTest(codePoint, category);
isISOControlTest(codePoint);
isIdentifierIgnorableTest(codePoint, category);
isJavaIdentifierStartTest(codePoint, category);
isJavaIdentifierPartTest(codePoint, category);
isUnicodeIdentifierStartTest(codePoint, category);
isUnicodeIdentifierPartTest(codePoint, category);
}
private static void isDigitTest(int codePoint, String category) {
boolean actual = Character.isDigit(codePoint);
boolean expected = category.equals("Nd");
if (actual != expected) {
printDiff(codePoint, "isDigit", actual, expected);
}
}
private static void isLetterTest(int codePoint, String category) {
boolean actual = Character.isLetter(codePoint);
boolean expected = isLetter(category);
if (actual != expected) {
printDiff(codePoint, "isLetter", actual, expected);
}
}
private static void isLetterOrDigitTest(int codePoint, String category) {
boolean actual = Character.isLetterOrDigit(codePoint);
boolean expected = isLetter(category) || category.equals("Nd");
if (actual != expected) {
printDiff(codePoint, "isLetterOrDigit", actual, expected);
}
}
private static void isSpaceCharTest(int codePoint, String category) {
boolean actual = Character.isSpaceChar(codePoint);
boolean expected = isSpaceChar(category);
if (actual != expected) {
printDiff(codePoint, "isSpaceChar", actual, expected);
}
}
private static void isWhitespaceTest(int codePoint, String category) {
boolean actual = Character.isWhitespace(codePoint);
boolean expected = isWhitespace(codePoint, category);
if (actual != expected) {
printDiff(codePoint, "isWhitespace", actual, expected);
}
}
private static void isTitleCaseTest(int codePoint, String category) {
boolean actual = Character.isTitleCase(codePoint);
boolean expected = category.equals("Lt");
if (actual != expected) {
printDiff(codePoint, "isTitleCase", actual, expected);
}
}
private static void isISOControlTest(int codePoint) {
boolean actual = Character.isISOControl(codePoint);
boolean expected = isISOControl(codePoint);
if (actual != expected) {
printDiff(codePoint, "isISOControl", actual, expected);
}
}
private static void isIdentifierIgnorableTest(int codePoint, String category) {
boolean actual = Character.isIdentifierIgnorable(codePoint);
boolean expected = isIdentifierIgnorable(codePoint, category);
if (actual != expected) {
printDiff(codePoint, "isIdentifierIgnorable", actual, expected);
}
}
private static void isJavaIdentifierStartTest(int codePoint, String category) {
boolean actual = Character.isJavaIdentifierStart(codePoint);
boolean expected = isJavaIdentifierStart(category);
if (actual != expected) {
printDiff(codePoint, "isJavaIdentifierStart", actual, expected);
}
}
private static void isJavaIdentifierPartTest(int codePoint, String category) {
boolean actual = Character.isJavaIdentifierPart(codePoint);
boolean expected = isJavaIdentifierPart(codePoint, category);
if (actual != expected) {
printDiff(codePoint, "isJavaIdentifierPart", actual, expected);
}
}
private static void isUnicodeIdentifierStartTest(int codePoint, String category) {
boolean actual = Character.isUnicodeIdentifierStart(codePoint);
boolean expected = isUnicodeIdentifierStart(codePoint, category);
if (actual != expected) {
printDiff(codePoint, "isUnicodeIdentifierStart", actual, expected);
}
}
private static void isUnicodeIdentifierPartTest(int codePoint, String category) {
boolean actual = Character.isUnicodeIdentifierPart(codePoint);
boolean expected = isUnicodeIdentifierPart(codePoint, category);
if (actual != expected) {
printDiff(codePoint, "isUnicodeIdentifierPart", actual, expected);
}
}
private static boolean isLetter(String category) {
return category.equals("Lu") || category.equals("Ll")
|| category.equals("Lt") || category.equals("Lm")
|| category.equals("Lo");
}
private static boolean isSpaceChar(String category) {
return category.equals("Zs") || category.equals("Zl")
|| category.equals("Zp");
}
private static boolean isWhitespace(int codePoint, String category) {
if (isSpaceChar(category) && codePoint != Integer.parseInt("00A0", 16)
&& codePoint != Integer.parseInt("2007", 16)
&& codePoint != Integer.parseInt("202F", 16)) {
return true;
} else {
if (codePoint == Integer.parseInt("0009", 16)
|| codePoint == Integer.parseInt("000A", 16)
|| codePoint == Integer.parseInt("000B", 16)
|| codePoint == Integer.parseInt("000C", 16)
|| codePoint == Integer.parseInt("000D", 16)
|| codePoint == Integer.parseInt("001C", 16)
|| codePoint == Integer.parseInt("001D", 16)
|| codePoint == Integer.parseInt("001E", 16)
|| codePoint == Integer.parseInt("001F", 16)) {
return true;
}
}
return false;
}
private static boolean isISOControl(int codePoint) {
return (codePoint > 0x00 && codePoint < 0x1f)
|| (codePoint > 0x7f && codePoint < 0x9f)
|| (codePoint == 0x00 || codePoint == 0x1f || codePoint == 0x7f || codePoint == 0x9f);
}
private static boolean isIdentifierIgnorable(int codePoint, String category) {
if (category.equals("Cf")) {
return true;
} else {
int a1 = Integer.parseInt("0000", 16);
int a2 = Integer.parseInt("0008", 16);
int b1 = Integer.parseInt("000E", 16);
int b2 = Integer.parseInt("001B", 16);
int c1 = Integer.parseInt("007F", 16);
int c2 = Integer.parseInt("009F", 16);
if ((codePoint > a1 && codePoint < a2) || (codePoint > b1 && codePoint < b2)
|| (codePoint > c1 && codePoint < c2) || (codePoint == a1 || codePoint == a2
|| codePoint == b1 || codePoint == b2 || codePoint == c1 || codePoint == c2)) {
return true;
}
}
return false;
}
private static boolean isJavaIdentifierStart(String category) {
return isLetter(category) || category.equals("Nl") || category.equals("Sc")
|| category.equals("Pc");
}
private static boolean isJavaIdentifierPart(int codePoint, String category) {
return isLetter(category) || category.equals("Sc") || category.equals("Pc")
|| category.equals("Nd") || category.equals("Nl")
|| category.equals("Mc") || category.equals("Mn")
|| isIdentifierIgnorable(codePoint, category);
}
private static boolean isUnicodeIdentifierStart(int codePoint, String category) {
return isLetter(category) || category.equals("Nl")
|| isOtherIDStart(codePoint);
}
private static boolean isUnicodeIdentifierPart(int codePoint, String category) {
return isLetter(category) || category.equals("Pc") || category.equals("Nd")
|| category.equals("Nl") || category.equals("Mc") || category.equals("Mn")
|| isIdentifierIgnorable(codePoint, category)
|| isOtherIDStart(codePoint)
|| isOtherIDContinue(codePoint);
}
private static boolean isOtherIDStart(int codePoint) {
return codePoint == 0x1885 ||
codePoint == 0x1886 ||
codePoint == 0x2118 ||
codePoint == 0x212E ||
codePoint == 0x309B ||
codePoint == 0x309C;
}
private static boolean isOtherIDContinue(int codePoint) {
return codePoint == 0x00B7 ||
codePoint == 0x0387 ||
(codePoint >= 0x1369 && codePoint <= 0x1371) ||
codePoint == 0x19DA ||
codePoint == 0x200C ||
codePoint == 0x200D ||
codePoint == 0x30FB ||
codePoint == 0xFF65;
}
private static void printDiff(int codePoint, String method, boolean actual, boolean expected) {
System.out.println("Not equal at codePoint <" + Integer.toHexString(codePoint)
+ ">, method: " + method
+ ", actual: " + actual + ", expected: " + expected);
diffs++;
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8147531
* @summary Check j.l.Character.getName and codePointOf
*/
import java.util.Locale;
public class CharacterName {
public static void main(String[] args) {
for (int cp = 0; cp < Character.MAX_CODE_POINT; cp++) {
if (!Character.isValidCodePoint(cp)) {
try {
Character.getName(cp);
} catch (IllegalArgumentException x) {
continue;
}
throw new RuntimeException("Invalid failed: " + cp);
} else if (Character.getType(cp) == Character.UNASSIGNED) {
if (Character.getName(cp) != null)
throw new RuntimeException("Unsigned failed: " + cp);
} else {
String name = Character.getName(cp);
if (cp != Character.codePointOf(name) ||
cp != Character.codePointOf(name.toLowerCase(Locale.ENGLISH)))
throw new RuntimeException("Roundtrip failed: " + cp);
}
}
}
}

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 7037261 7070436 7198195 8032446 8072600 8221431 8229831
* @summary Check j.l.Character.isLowerCase/isUppercase/isAlphabetic/isIdeographic/
* isUnicodeIdentifierStart/isUnicodeIdentifierPart
* @library /lib/testlibrary/java/lang
*/
import java.util.regex.*;
import java.util.*;
import java.io.*;
import static java.lang.Character.*;
public class CheckProp {
public static void main(String[] args) {
Map<String, List<Integer>> propMap = new LinkedHashMap<>();
List.of(UCDFiles.PROP_LIST.toFile(), UCDFiles.DERIVED_PROPS.toFile()).stream()
.forEach(f -> readPropMap(propMap, f));
Integer[] otherLowercase = propMap.get("Other_Lowercase").toArray(new Integer[0]);
Integer[] otherUppercase = propMap.get("Other_Uppercase").toArray(new Integer[0]);
Integer[] otherAlphabetic = propMap.get("Other_Alphabetic").toArray(new Integer[0]);
Integer[] ideographic = propMap.get("Ideographic").toArray(new Integer[0]);
Integer[] IDStart = propMap.get("ID_Start").toArray(new Integer[0]);
Integer[] IDContinue = propMap.get("ID_Continue").toArray(new Integer[0]);
int fails = 0;
for (int cp = MIN_CODE_POINT; cp < MAX_CODE_POINT; cp++) {
int type = getType(cp);
if (isLowerCase(cp) !=
(type == LOWERCASE_LETTER ||
Arrays.binarySearch(otherLowercase, cp) >= 0))
{
fails++;
System.err.printf("Wrong isLowerCase(U+%04x)\n", cp);
}
if (isUpperCase(cp) !=
(type == UPPERCASE_LETTER ||
Arrays.binarySearch(otherUppercase, cp) >= 0))
{
fails++;
System.err.printf("Wrong isUpperCase(U+%04x)\n", cp);
}
if (isAlphabetic(cp) !=
(type == UPPERCASE_LETTER || type == LOWERCASE_LETTER ||
type == TITLECASE_LETTER || type == MODIFIER_LETTER ||
type == OTHER_LETTER || type == OTHER_LETTER ||
type == LETTER_NUMBER ||
Arrays.binarySearch(otherAlphabetic, cp) >=0))
{
fails++;
System.err.printf("Wrong isAlphabetic(U+%04x)\n", cp);
}
if (isIdeographic(cp) !=
(Arrays.binarySearch(ideographic, cp) >= 0))
{
fails++;
System.err.printf("Wrong isIdeographic(U+%04x)\n", cp);
}
if (isUnicodeIdentifierStart(cp) !=
(cp == 0x2E2F ||
Arrays.binarySearch(IDStart, cp) >= 0))
{
fails++;
System.err.printf("Wrong isUnicodeIdentifierStart(U+%04x)\n", cp);
}
if (isUnicodeIdentifierPart(cp) !=
(isIdentifierIgnorable(cp) ||
cp == 0x2E2F ||
Arrays.binarySearch(IDContinue, cp) >= 0))
{
fails++;
System.err.printf("Wrong isUnicodeIdentifierPart(U+%04x)\n", cp);
}
}
if (fails != 0)
throw new RuntimeException("CheckProp failed=" + fails);
}
private static void readPropMap(Map<String, List<Integer>> propMap, File fPropList) {
try {
BufferedReader sbfr = new BufferedReader(new FileReader(fPropList));
Matcher m = Pattern.compile("(\\p{XDigit}+)(?:\\.{2}(\\p{XDigit}+))?\\s*;\\s+(\\w+)\\s+#.*").matcher("");
String line = null;
int lineNo = 0;
while ((line = sbfr.readLine()) != null) {
lineNo++;
if (line.length() <= 1 || line.charAt(0) == '#') {
continue;
}
m.reset(line);
if (m.matches()) {
int start = Integer.parseInt(m.group(1), 16);
int end = (m.group(2)==null)?start
:Integer.parseInt(m.group(2), 16);
String name = m.group(3);
List<Integer> list = propMap.get(name);
if (list == null) {
list = new ArrayList<Integer>();
propMap.put(name, list);
}
while (start <= end)
list.add(start++);
} else {
System.out.printf("Warning: Unrecognized line %d <%s>%n", lineNo, line);
}
}
sbfr.close();
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
//for (String name: propMap.keySet()) {
// System.out.printf("%s %d%n", name, propMap.get(name).size());
//}
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 6945564 6959267 7033561 7070436 7198195 8032446 8072600 8221431
* @summary Check that the j.l.Character.UnicodeScript
* @library /lib/testlibrary/java/lang
*/
import java.io.*;
import java.util.*;
import java.util.regex.*;
import java.lang.Character.UnicodeScript;
public class CheckScript {
public static void main(String[] args) throws Exception {
File fScripts;
File fAliases;
if (args.length == 0) {
fScripts = UCDFiles.SCRIPTS.toFile();
fAliases = UCDFiles.PROPERTY_VALUE_ALIASES.toFile();
} else if (args.length == 2) {
fScripts = new File(args[0]);
fAliases = new File(args[1]);
} else {
System.out.println("java CharacterScript Scripts.txt PropertyValueAliases.txt");
throw new RuntimeException("Datafile name should be specified.");
}
Matcher m = Pattern.compile("(\\p{XDigit}+)(?:\\.{2}(\\p{XDigit}+))?\\s+;\\s+(\\w+)\\s+#.*").matcher("");
String line = null;
HashMap<String,ArrayList<Integer>> scripts = new HashMap<>();
try (BufferedReader sbfr = new BufferedReader(new FileReader(fScripts))) {
while ((line = sbfr.readLine()) != null) {
if (line.length() <= 1 || line.charAt(0) == '#') {
continue;
}
m.reset(line);
if (m.matches()) {
int start = Integer.parseInt(m.group(1), 16);
int end = (m.group(2)==null)?start
:Integer.parseInt(m.group(2), 16);
String name = m.group(3).toLowerCase(Locale.ENGLISH);
ArrayList<Integer> ranges = scripts.get(name);
if (ranges == null) {
ranges = new ArrayList<Integer>();
scripts.put(name, ranges);
}
ranges.add(start);
ranges.add(end);
}
}
}
// check all defined ranges
Integer[] ZEROSIZEARRAY = new Integer[0];
for (String name : scripts.keySet()) {
System.out.println("Checking " + name + "...");
Integer[] ranges = scripts.get(name).toArray(ZEROSIZEARRAY);
Character.UnicodeScript expected =
Character.UnicodeScript.forName(name);
int off = 0;
while (off < ranges.length) {
int start = ranges[off++];
int end = ranges[off++];
for (int cp = start; cp <= end; cp++) {
Character.UnicodeScript script =
Character.UnicodeScript.of(cp);
if (script != expected) {
throw new RuntimeException(
"UnicodeScript failed: cp=" +
Integer.toHexString(cp) +
", of(cp)=<" + script + "> but <" +
expected + "> is expected");
}
}
}
}
// check all codepoints
for (int cp = 0; cp < Character.MAX_CODE_POINT; cp++) {
Character.UnicodeScript script = Character.UnicodeScript.of(cp);
if (script == Character.UnicodeScript.UNKNOWN) {
if (Character.getType(cp) != Character.UNASSIGNED &&
Character.getType(cp) != Character.SURROGATE &&
Character.getType(cp) != Character.PRIVATE_USE)
throw new RuntimeException(
"UnicodeScript failed: cp=" +
Integer.toHexString(cp) +
", of(cp)=<" + script + "> but UNKNOWN is expected");
} else {
Integer[] ranges =
scripts.get(script.name().toLowerCase(Locale.ENGLISH))
.toArray(ZEROSIZEARRAY);
int off = 0;
boolean found = false;
while (off < ranges.length) {
int start = ranges[off++];
int end = ranges[off++];
if (cp >= start && cp <= end)
found = true;
}
if (!found) {
throw new RuntimeException(
"UnicodeScript failed: cp=" +
Integer.toHexString(cp) +
", of(cp)=<" + script +
"> but NOT in ranges of this script");
}
}
}
// check all aliases
m = Pattern.compile("sc\\s*;\\s*(\\p{Alpha}{4})\\s*;\\s*([\\p{Alpha}|_]+)\\s*.*").matcher("");
line = null;
try (BufferedReader sbfr = new BufferedReader(new FileReader(fAliases))) {
while ((line = sbfr.readLine()) != null) {
if (line.length() <= 1 || line.charAt(0) == '#') {
continue;
}
m.reset(line);
if (m.matches()) {
String alias = m.group(1);
String name = m.group(2);
// HRKT -> Katakana_Or_Hiragana not supported
if ("HRKT".equals(alias.toUpperCase(Locale.ENGLISH)))
continue;
if (Character.UnicodeScript.forName(alias) !=
Character.UnicodeScript.forName(name)) {
throw new RuntimeException(
"UnicodeScript failed: alias<" + alias +
"> does not map to <" + name + ">");
}
}
}
}
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4114080 6565620 6959267 7070436 7198195 8032446 8072600 8221431
* @summary Make sure the attributes of Unicode characters, as
* returned by the Character API, are as expected. Do this by
* comparing them to a baseline file together with a list of
* known diffs.
* @library /lib/testlibrary/java/lang
* @build UnicodeSpec CharCheck
* @run main CheckUnicode
* @author Alan Liu
* @author John O'Conner
*/
import java.io.*;
public class CheckUnicode {
public static void main(String args[]) throws Exception {
// 1. Check that the current 12.1 spec file is handled by the current
// version of Character.
File unicodeSpec = UCDFiles.UNICODE_DATA.toFile();
for (int x = 0; x < 16; ++x) {
int diffs = CharCheck.check(x, unicodeSpec);
if (diffs != 0) {
throw new RuntimeException("Unicode properties have changed " +
"in an unexpected way");
}
}
// 2. Check that Java identifiers are recognized correctly.
// test a few characters that are good id starts
char[] idStartChar = {'$', '\u20AC', 'a', 'A', 'z', 'Z', '_', '\u0E3F',
'\u1004', '\u10A0', '\u3400', '\u4E00', '\uAC00' };
for (int x = 0; x < idStartChar.length; x++) {
if (Character.isJavaIdentifierStart(idStartChar[x]) != true) {
throw new RuntimeException("Java id start characters are not recognized.");
}
}
// test a few characters that are good id parts
char[] idPartChar = {'0', '9', '\u0000', '\u0008', '\u000E', '\u007F'};
for (int x=0; x< idStartChar.length; x++) {
if (Character.isJavaIdentifierPart(idStartChar[x]) != true) {
throw new RuntimeException("Java id part characters are not recognized.");
}
}
for (int x=0; x<idPartChar.length; x++) {
if (Character.isJavaIdentifierPart(idPartChar[x]) != true) {
throw new RuntimeException("Java id part characters are not recognized.");
}
}
// now do some negative checks
for (int x=0; x< idPartChar.length; x++) {
if (Character.isJavaIdentifierStart(idPartChar[x]) != false) {
throw new RuntimeException("These Java id part characters" +
"should not be start characters.");
}
}
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
*
* @author Martin Buchholz
*/
import java.util.*;
import static java.lang.Character.*;
public class DumpCharProperties {
final static Locale turkish = Locale.of("tr");
static String charProps(int i) {
String s = new String(new int[]{i},0,1);
return String.format
("%b %b %b %b %b %b %b %b %b %b %b %b %d %d %d %d %d %b %b %d %d %b %d %d",
isLowerCase(i),
isUpperCase(i),
isTitleCase(i),
isDigit(i),
isDefined(i),
isLetter(i),
isLetterOrDigit(i),
isJavaIdentifierStart(i),
isJavaIdentifierPart(i),
isUnicodeIdentifierStart(i),
isUnicodeIdentifierPart(i),
isIdentifierIgnorable(i),
toLowerCase(i),
toUpperCase(i),
toTitleCase(i),
digit(i, 16),
getNumericValue(i),
isSpaceChar(i),
isWhitespace(i),
getType(i),
getDirectionality(i),
isMirrored(i),
(int) s.toUpperCase(Locale.GERMAN).charAt(0),
(int) s.toUpperCase(turkish).charAt(0));
}
public static void main(String[] args) throws Throwable {
for (int i = 0; i < 17*0x10000; i++) {
System.out.println(charProps(i));
}
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
/**
* @test
* @bug 8302877
* @summary Provides exhaustive verification of Character.toUpperCase and Character.toLowerCase
* for all code points in the latin1 range 0-255.
* @run junit Latin1CaseConversion
*/
public class Latin1CaseConversion {
@Test
public void shouldUpperCaseAndLowerCaseLatin1() {
for (int c = 0; c < 256; c++) {
int upper = Character.toUpperCase(c);
int lower = Character.toLowerCase(c);
if (c < 0x41) { // Before A
assertUnchanged(upper, lower, c);
} else if (c <= 0x5A) { // A-Z
assertEquals(c, upper);
assertEquals(c + 32, lower);
} else if (c < 0x61) { // Between Z and a
assertUnchanged(upper, lower, c);
} else if (c <= 0x7A) { // a-z
assertEquals(c - 32, upper);
assertEquals(c, lower);
} else if (c < 0xB5) { // Between z and Micro Sign
assertUnchanged(upper, lower, c);
} else if (c == 0xB5) { // Special case for Micro Sign
assertEquals(0x39C, upper);
assertEquals(c, lower);
} else if (c < 0xC0) { // Between my and A-grave
assertUnchanged(upper, lower, c);
} else if (c < 0xD7) { // A-grave - O with Diaeresis
assertEquals(c, upper);
assertEquals(c + 32, lower);
} else if (c == 0xD7) { // Multiplication
assertUnchanged(upper, lower, c);
} else if (c <= 0xDE) { // O with slash - Thorn
assertEquals(c, upper);
assertEquals(c + 32, lower);
} else if (c == 0xDF) { // Sharp s
assertUnchanged(upper, lower, c);
} else if (c < 0xF7) { // a-grave - divsion
assertEquals(c - 32, upper);
assertEquals(c, lower);
} else if (c == 0xF7) { // Division
assertUnchanged(upper, lower, c);
} else if (c < 0xFF) { // o with slash - thorn
assertEquals(c - 32, upper);
assertEquals(c, lower);
} else if (c == 0XFF) { // Special case for y with Diaeresis
assertEquals(0x178, upper);
assertEquals(c, lower);
} else {
fail("Uncovered code point: " + Integer.toHexString(c));
}
}
}
private static void assertUnchanged(int upper, int lower, int c) {
assertEquals(c, upper);
assertEquals(c, lower);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8196740
* @summary Check j.l.Character.digit(int,int) for Latin1 characters
*/
public class Latin1Digit {
public static void main(String[] args) throws Exception {
for (int ch = 0; ch < 256; ++ch) {
for (int radix = -256; radix <= 256; ++radix) {
test(ch, radix);
}
test(ch, Integer.MIN_VALUE);
test(ch, Integer.MAX_VALUE);
}
}
static void test(int ch, int radix) throws Exception {
int d1 = Character.digit(ch, radix);
int d2 = canonicalDigit(ch, radix);
if (d1 != d2) {
throw new Exception("Wrong result for char="
+ ch + " (" + (char)ch + "), radix="
+ radix + "; " + d1 + " != " + d2);
}
}
// canonical version of Character.digit(int,int) for Latin1
static int canonicalDigit(int ch, int radix) {
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
return -1;
}
if (ch >= '0' && ch <= '9' && ch < (radix + '0')) {
return ch - '0';
}
if (ch >= 'A' && ch <= 'Z' && ch < (radix + 'A' - 10)) {
return ch - 'A' + 10;
}
if (ch >= 'a' && ch <= 'z' && ch < (radix + 'a' - 10)) {
return ch - 'a' + 10;
}
return -1;
}
}

View file

@ -0,0 +1,893 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4533872 4985214 4985217 4993841 5017268 5017280 8298033
* @summary Unit tests for supplementary character support (JSR-204)
* @compile Supplementary.java
* @run main/timeout=600 Supplementary
*/
public class Supplementary {
private static final char MIN_HIGH = '\uD800';
private static final char MAX_HIGH = '\uDBFF';
private static final char MIN_LOW = MAX_HIGH + 1;
private static final char MAX_LOW = '\uDFFF';
private static final int MIN_CODE_POINT = 0x000000;
private static final int MIN_SUPPLEMENTARY = 0x010000;
private static final int MAX_SUPPLEMENTARY = 0x10ffff;
public static void main(String[] args) {
// Do not change the order of test method calls since there
// are some interdependencies.
testConstants();
test00();
// Store all Unicode code points, except for surrogate code
// points, in cu[] through the loops below. Then, use the data
// for code point/code unit conversion and other tests later.
char[] cu = new char[(MAX_SUPPLEMENTARY+1) * 2];
int length = test01(cu);
String str = new String(cu, 0, length);
cu = null;
test02(str);
test03(str.toCharArray());
test04(str);
test05(str);
// Test for toString(int)
test06();
// Test unpaired surrogates
testUnpaired();
// Test exceptions
testExceptions00();
testExceptions01(str);
testExceptions02(str.toCharArray());
}
static void testConstants() {
if (Character.MIN_HIGH_SURROGATE != MIN_HIGH) {
constantError("MIN_HIGH_SURROGATE", Character.MIN_HIGH_SURROGATE, MIN_HIGH);
}
if (Character.MAX_HIGH_SURROGATE != MAX_HIGH) {
constantError("MAX_HIGH_SURROGATE", Character.MAX_HIGH_SURROGATE, MAX_HIGH);
}
if (Character.MIN_LOW_SURROGATE != MIN_LOW) {
constantError("MIN_LOW_SURROGATE", Character.MIN_LOW_SURROGATE, MIN_LOW);
}
if (Character.MAX_LOW_SURROGATE != MAX_LOW) {
constantError("MAX_LOW_SURROGATE", Character.MAX_LOW_SURROGATE, MAX_LOW);
}
if (Character.MIN_SURROGATE != MIN_HIGH) {
constantError("MIN_SURROGATE", Character.MIN_SURROGATE, MIN_HIGH);
}
if (Character.MAX_SURROGATE != MAX_LOW) {
constantError("MAX_SURROGATE", Character.MAX_SURROGATE, MAX_LOW);
}
if (Character.MIN_SUPPLEMENTARY_CODE_POINT != MIN_SUPPLEMENTARY) {
constantError("MIN_SUPPLEMENTARY_CODE_POINT",
Character.MIN_SUPPLEMENTARY_CODE_POINT, MIN_SUPPLEMENTARY);
}
if (Character.MIN_CODE_POINT != MIN_CODE_POINT) {
constantError("MIN_CODE_POINT", Character.MIN_CODE_POINT, MIN_CODE_POINT);
}
if (Character.MAX_CODE_POINT != MAX_SUPPLEMENTARY) {
constantError("MAX_CODE_POINT", Character.MAX_CODE_POINT, MAX_SUPPLEMENTARY);
}
}
static void constantError(String name, int value, int expectedValue) {
throw new RuntimeException("Character." + name + " has a wrong value: got "
+ toHexString(value)
+ ", expected " + toHexString(expectedValue));
}
/*
* Test isValidCodePoint(int)
* isSupplementaryCodePoint(int)
* charCount(int)
*/
static void test00() {
for (int cp = -MAX_SUPPLEMENTARY; cp <= MAX_SUPPLEMENTARY*2; cp++) {
boolean isValid = cp >= 0 && cp <= MAX_SUPPLEMENTARY;
if (Character.isValidCodePoint(cp) != isValid) {
throw new RuntimeException("isValidCodePoint failed with "
+ toHexString(cp));
}
boolean isSupplementary = cp >= MIN_SUPPLEMENTARY && cp <= MAX_SUPPLEMENTARY;
if (Character.isSupplementaryCodePoint(cp) != isSupplementary) {
throw new RuntimeException("isSupplementaryCodePoint failed with "
+ toHexString(cp));
}
int len = Character.charCount(cp);
if (isValid) {
if ((isSupplementary && len != 2)
|| (!isSupplementary && len != 1)) {
throw new RuntimeException("wrong character length "+len+" for "
+ toHexString(cp));
}
} else if (len != 1 && len != 2) {
throw new RuntimeException("wrong character length "+len+" for "
+ toHexString(cp));
}
}
}
/**
* Test toChar(int)
* toChar(int, char[], int)
* isHighSurrogate(char)
* isLowSurrogate(char)
* isSurrogatePair(int, int)
*
* While testing those methods, this method generates all Unicode
* code points (except for surrogate code points) and store them
* in cu.
*
* @return the number of code units generated in cu
*/
static int test01(char[] cu) {
int index = 0;
// Test toChar(int)
// toChar(int, char[], int)
// isHighSurrogate(char)
// isLowSurrogate(char)
// with BMP code points
for (int i = 0; i <= Character.MAX_VALUE; i++) {
char[] u = Character.toChars(i);
if (u.length != 1 || u[0] != i) {
throw new RuntimeException("wrong toChars(int) result for BMP: "
+ toHexString("u", u));
}
int n = Character.toChars(i, cu, index);
if (n != 1 || cu[index] != i) {
throw new RuntimeException("wrong toChars(int, char[], int) result for BMP:"
+ " len=" + n
+ ", cu["+index+"]="+toHexString(cu[index]));
}
boolean isHigh = i >= MIN_HIGH && i <= MAX_HIGH;
if (Character.isHighSurrogate((char) i) != isHigh) {
throw new RuntimeException("wrong high-surrogate test for "
+ toHexString(i));
}
boolean isLow = i >= MIN_LOW && i <= MAX_LOW;
if (Character.isLowSurrogate((char)i) != isLow) {
throw new RuntimeException("wrong low-surrogate test for "
+ toHexString(i));
}
if (!isHigh && !isLow) {
index++;
}
}
// Test isSurrogatePair with all surrogate pairs
// Test toChars(int)
// toChars(int, char[], int)
// with all supplementary characters
int supplementary = MIN_SUPPLEMENTARY;
for (int i = Character.MAX_VALUE/2; i <= Character.MAX_VALUE; i++) {
char hi = (char) i;
boolean isHigh = Character.isHighSurrogate(hi);
for (int j = Character.MAX_VALUE/2; j <= Character.MAX_VALUE; j++) {
char lo = (char) j;
boolean isLow = Character.isLowSurrogate(lo);
boolean isSurrogatePair = isHigh && isLow;
if (Character.isSurrogatePair(hi, lo) != isSurrogatePair) {
throw new RuntimeException("wrong surrogate pair test for hi="
+ toHexString(hi)
+ ", lo="+toHexString(lo));
}
if (isSurrogatePair) {
int cp = Character.toCodePoint(hi, lo);
if (cp != supplementary) {
throw new RuntimeException("wrong code point: got "
+ toHexString(cp)
+ ", expected="
+ toHexString(supplementary));
}
char[] u = Character.toChars(cp);
if (u.length != 2 || u[0] != hi || u[1] != lo) {
throw new RuntimeException("wrong toChars(int) result for supplementary: "+
toHexString("u", u));
}
int n = Character.toChars(cp, cu, index);
if (n != 2 || cu[index] != hi || cu[index+1] != lo) {
throw new RuntimeException("wrong toChars(int, char[], int) result "
+ "for supplementary: len=" + n
+ ", cu["+index+"]=" + toHexString(cu[index])
+ ", cu["+(index+1)+"]=" + toHexString(cu[index+1]));
}
index += n;
supplementary++;
}
}
}
if (supplementary != MAX_SUPPLEMENTARY + 1) {
throw new RuntimeException("wrong supplementary count (supplementary="
+ toHexString(supplementary)+")");
}
int nCodeUnits = Character.MAX_VALUE + 1 - (MAX_LOW - MIN_HIGH + 1)
+ ((MAX_SUPPLEMENTARY - MIN_SUPPLEMENTARY + 1) * 2);
if (index != nCodeUnits) {
throw new RuntimeException("wrong number of code units: " + index
+ ", expected " + nCodeUnits);
}
return index;
}
/**
* Test codePointAt(CharSequence, int)
* codePointBefore(CharSequence, int)
*/
static void test02(CharSequence cs) {
int cp = 0;
int ch;
for (int i = 0; i < cs.length(); i += Character.charCount(ch)) {
ch = Character.codePointAt(cs, i);
if (ch != cp) {
throw new RuntimeException("wrong codePointAt(CharSequence, "+i+") value: got "
+ toHexString(ch)
+ ", expected "+toHexString(cp));
}
cp++;
// Skip surrogates
if (cp == MIN_HIGH) {
cp = MAX_LOW + 1;
}
}
cp--;
for (int i = cs.length(); i > 0; i -= Character.charCount(ch)) {
ch = Character.codePointBefore(cs, i);
if (ch != cp) {
throw new RuntimeException("codePointBefore(CharSequence, "+i+") returned "
+ toHexString(ch)
+ ", expected " + toHexString(cp));
}
cp--;
// Skip surrogates
if (cp == MAX_LOW) {
cp = MIN_HIGH - 1;
}
}
}
/**
* Test codePointAt(char[], int)
* codePointAt(char[], int, int)
* codePointBefore(char[], int)
* codePointBefore(char[], int, int)
*/
static void test03(char[] a) {
int cp = 0;
int ch;
for (int i = 0; i < a.length; i += Character.charCount(ch)) {
ch = Character.codePointAt(a, i);
if (ch != cp) {
throw new RuntimeException("codePointAt(char[], "+i+") returned "
+ toHexString(ch)
+ ", expected "+toHexString(cp));
}
int x = Character.codePointAt(a, i, i+1);
if (x != a[i]) {
throw new RuntimeException(String.format(
"codePointAt(char[], %d, %d) returned 0x%04x, expected 0x%04x\n",
i, i+1, x, (int)a[i]));
}
cp++;
// Skip surrogates
if (cp == MIN_HIGH) {
cp = MAX_LOW + 1;
}
}
cp--;
for (int i = a.length; i > 0; i -= Character.charCount(ch)) {
ch = Character.codePointBefore(a, i);
if (ch != cp) {
throw new RuntimeException("codePointBefore(char[], "+i+") returned "
+ toHexString(ch)
+ ", expected " + toHexString(cp));
}
int x = Character.codePointBefore(a, i, i-1);
if (x != a[i-1]) {
throw new RuntimeException(String.format(
"codePointAt(char[], %d, %d) returned 0x%04x, expected 0x%04x\n",
i, i-1, x, (int)a[i-1]));
}
cp--;
// Skip surrogates
if (cp == MAX_LOW) {
cp = MIN_HIGH - 1;
}
}
}
/**
* Test codePointCount(CharSequence, int, int)
* codePointCount(char[], int, int, int, int)
*/
static void test04(String str) {
int length = str.length();
char[] a = str.toCharArray();
for (int i = 0; i <= length; i += 99, length -= 29999) {
int n = Character.codePointCount(str, i, length);
int m = codePointCount(str.substring(i, length));
checkCodePointCount(str, n, m);
n = Character.codePointCount(a, i, length - i);
checkCodePointCount(a, n, m);
}
// test special cases
length = str.length();
int n = Character.codePointCount(str, 0, 0);
checkCodePointCount(str, n, 0);
n = Character.codePointCount(str, length, length);
checkCodePointCount(str, n, 0);
n = Character.codePointCount(a, 0, 0);
checkCodePointCount(a, n, 0);
n = Character.codePointCount(a, length, 0);
checkCodePointCount(a, n, 0);
}
// This method assumes that Character.codePointAt() and
// Character.charCount() work correctly.
private static int codePointCount(CharSequence seq) {
int n = 0, len;
for (int i = 0; i < seq.length(); i += len) {
int codepoint = Character.codePointAt(seq, i);
n++;
len = Character.charCount(codepoint);
}
return n;
}
private static void checkCodePointCount(Object data, int n, int expected) {
String type = getType(data);
if (n != expected) {
throw new RuntimeException("codePointCount(" + type + "...) returned " + n
+ ", expected " + expected);
}
}
/**
* Test offsetByCodePoints(CharSequence, int, int)
* offsetByCodePoints(char[], int, int, int, int)
*
* This test case assumes that Character.codePointCount()s work
* correctly.
*/
static void test05(String str) {
int length = str.length();
char[] a = str.toCharArray();
for (int i = 0; i <= length; i += 99, length -= 29999) {
int nCodePoints = Character.codePointCount(a, i, length - i);
int index;
// offsetByCodePoints(CharSequence, int, int)
int expectedHighIndex = length;
// For forward CharSequence scan, we need to adjust the
// expected index in case the last char in the text range
// is a high surrogate and forms a valid supplementary
// code point with the next char.
if (length < a.length) {
int cp = Character.codePointAt(a, length - 1);
if (Character.isSupplementaryCodePoint(cp)) {
expectedHighIndex++;
}
}
index = Character.offsetByCodePoints(str, i, nCodePoints);
checkNewIndex(str, nCodePoints, index, expectedHighIndex);
int expectedLowIndex = i;
if (i > 0) {
int cp = Character.codePointBefore(a, i + 1);
if (Character.isSupplementaryCodePoint(cp)) {
expectedLowIndex--;
}
}
index = Character.offsetByCodePoints(str, length, -nCodePoints);
checkNewIndex(str, -nCodePoints, index, expectedLowIndex);
// offsetByCodePoints(char[], int, int, int, int)
int start = Math.max(0, i-1);
int limit = Math.min(a.length, length+1);
index = Character.offsetByCodePoints(a, start, limit - start,
i, nCodePoints);
checkNewIndex(a, nCodePoints, index, expectedHighIndex);
if (length != expectedHighIndex) {
index = Character.offsetByCodePoints(a, start, length - start,
i, nCodePoints);
checkNewIndex(a, nCodePoints, index, length);
}
index = Character.offsetByCodePoints(a, start, limit - start,
length, -nCodePoints);
checkNewIndex(a, -nCodePoints, index, expectedLowIndex);
if (i != expectedLowIndex) {
index = Character.offsetByCodePoints(a, i, limit - i,
length, -nCodePoints);
checkNewIndex(a, -nCodePoints, index, i);
}
}
// test special cases for 0-length text ranges.
length = str.length();
int index = Character.offsetByCodePoints(str, 0, 0);
checkNewIndex(str, 0, index, 0);
index = Character.offsetByCodePoints(str, length, 0);
checkNewIndex(str, 0, index, length);
index = Character.offsetByCodePoints(a, 0, 0, 0, 0);
checkNewIndex(a, 0, index, 0);
index = Character.offsetByCodePoints(a, 0, length, 0, 0);
checkNewIndex(a, 0, index, 0);
index = Character.offsetByCodePoints(a, 0, length, length, 0);
checkNewIndex(a, 0, index, length);
index = Character.offsetByCodePoints(a, length, 0, length, 0);
checkNewIndex(a, 0, index, length);
}
/**
* Test toString(int)
*
* This test case assumes that Character.toChars()/String(char[]) work
* correctly.
*/
static void test06() {
for (int cp = Character.MIN_CODE_POINT; cp <= Character.MAX_CODE_POINT; cp++) {
String result = Character.toString(cp);
String expected = new String(Character.toChars(cp));
if (!result.equals(expected)) {
throw new RuntimeException("Wrong string is created. code point: " +
cp + ", result: " + result + ", expected: " + expected);
}
}
}
private static void checkNewIndex(Object data, int offset, int result, int expected) {
String type = getType(data);
String offsetType = (offset > 0) ? "positive" : (offset < 0) ? "negative" : "0";
if (result != expected) {
throw new RuntimeException("offsetByCodePoints(" + type + ", ...) ["
+ offsetType + " offset]"
+ " returned " + result
+ ", expected " + expected);
}
}
// Test codePointAt(CharSequence, int)
// codePointBefore(CharSequence, int)
// codePointAt(char[], int)
// codePointBefore(char[], int)
// toChar(int)
// toChar(int, char[], int)
// with unpaired surrogates
static void testUnpaired() {
testCodePoint("\uD800", new int[] { 0xD800 });
testCodePoint("\uDC00", new int[] { 0xDC00 });
testCodePoint("a\uD800", new int[] { 'a', 0xD800 });
testCodePoint("a\uDC00", new int[] { 'a', 0xDC00 });
testCodePoint("\uD800a", new int[] { 0xD800, 'a' });
testCodePoint("\uDBFFa", new int[] { 0xDBFF, 'a' });
testCodePoint("a\uD800\uD801", new int[] { 'a', 0xD800, 0xD801 });
testCodePoint("a\uD800x\uDC00", new int[] { 'a', 0xD800, 'x', 0xDC00 });
testCodePoint("\uDC00\uD800", new int[] { 0xDC00, 0xD800 });
testCodePoint("\uD800\uDC00\uDC00", new int[] { 0x10000, 0xDC00 });
testCodePoint("\uD800\uD800\uDC00", new int[] { 0xD800, 0x10000 });
testCodePoint("\uD800\uD800\uD800\uD800\uDC00\uDC00\uDC00\uDC00",
new int[] { 0xD800, 0xD800, 0xD800, 0x10000, 0xDC00, 0xDC00, 0xDC00});
}
static void testCodePoint(String str, int[] codepoints) {
int c;
// Test Character.codePointAt/Before(CharSequence, int)
int j = 0;
for (int i = 0; i < str.length(); i += Character.charCount(c)) {
c = Character.codePointAt(str, i);
if (c != codepoints[j++]) {
throw new RuntimeException("codePointAt(CharSequence, " + i + ") returned "
+ toHexString(c)
+ ", expected " + toHexString(codepoints[j-1]));
}
}
if (j != codepoints.length) {
throw new RuntimeException("j != codepoints.length after codePointAt(CharSequence, int)"
+ " (j=" + j + ")"
+ ", expected: " + codepoints.length);
}
j = codepoints.length;
for (int i = str.length(); i > 0 ; i -= Character.charCount(c)) {
c = Character.codePointBefore(str, i);
if (c != codepoints[--j]) {
throw new RuntimeException("codePointBefore(CharSequence, " + i + ") returned "
+ toHexString(c)
+ ", expected " + toHexString(codepoints[j]));
}
}
if (j != 0) {
throw new RuntimeException("j != 0 after codePointBefore(CharSequence, int)"
+ " (j=" + j + ")");
}
// Test Character.codePointAt/Before(char[], int)
char[] a = str.toCharArray();
j = 0;
for (int i = 0; i < a.length; i += Character.charCount(c)) {
c = Character.codePointAt(a, i);
if (c != codepoints[j++]) {
throw new RuntimeException("codePointAt(char[], " + i + ") returned "
+ toHexString(c)
+ ", expected " + toHexString(codepoints[j-1]));
}
}
if (j != codepoints.length) {
throw new RuntimeException("j != codepoints.length after codePointAt(char[], int)"
+ " (j=" + j + ")"
+ ", expected: " + codepoints.length);
}
j = codepoints.length;
for (int i = a.length; i > 0 ; i -= Character.charCount(c)) {
c = Character.codePointBefore(a, i);
if (c != codepoints[--j]) {
throw new RuntimeException("codePointBefore(char[], " + i + ") returned "
+ toHexString(c)
+ ", expected " + toHexString(codepoints[j]));
}
}
if (j != 0) {
throw new RuntimeException("j != 0 after codePointBefore(char[], int)"
+ " (j=" + j + ")");
}
// Test toChar(int)
j = 0;
for (int i = 0; i < codepoints.length; i++) {
a = Character.toChars(codepoints[i]);
for (int k = 0; k < a.length; k++) {
if (str.charAt(j++) != a[k]) {
throw new RuntimeException("toChars(int) returned " + toHexString("result", a)
+ " from codepoint=" + toHexString(codepoints[i]));
}
}
}
// Test toChars(int, char[], int)
a = new char[codepoints.length * 2];
j = 0;
for (int i = 0; i < codepoints.length; i++) {
int n = Character.toChars(codepoints[i], a, j);
j += n;
}
String s = new String(a, 0, j);
if (!str.equals(s)) {
throw new RuntimeException("toChars(int, char[], int) returned "
+ toHexString("dst", s.toCharArray())
+ ", expected " + toHexString("data", str.toCharArray()));
}
}
// Test toChar(int)
// toChar(int, char[], int)
// toString(int)
// for exceptions
static void testExceptions00() {
callToChars1(-1, IllegalArgumentException.class);
callToChars1(MAX_SUPPLEMENTARY + 1, IllegalArgumentException.class);
callToChars3(MAX_SUPPLEMENTARY, null, 0, NullPointerException.class);
callToChars3(-MIN_SUPPLEMENTARY, new char[2], 0, IllegalArgumentException.class);
callToChars3(MAX_SUPPLEMENTARY + 1, new char[2], 0, IllegalArgumentException.class);
callToChars3('A', new char[0], 0, IndexOutOfBoundsException.class);
callToChars3('A', new char[1], -1, IndexOutOfBoundsException.class);
callToChars3('A', new char[1], 1, IndexOutOfBoundsException.class);
callToChars3(MIN_SUPPLEMENTARY, new char[0], 0, IndexOutOfBoundsException.class);
callToChars3(MIN_SUPPLEMENTARY, new char[1], 0, IndexOutOfBoundsException.class);
callToChars3(MIN_SUPPLEMENTARY, new char[2], -1, IndexOutOfBoundsException.class);
callToChars3(MIN_SUPPLEMENTARY, new char[2], 1, IndexOutOfBoundsException.class);
callToString(Character.MIN_CODE_POINT - 1, IllegalArgumentException.class);
callToString(Character.MAX_CODE_POINT + 1, IllegalArgumentException.class);
}
static final boolean At = true, Before = false;
/**
* Test codePointAt(CharSequence, int)
* codePointBefore(CharSequence, int)
* codePointCount(CharSequence, int, int)
* offsetByCodePoints(CharSequence, int, int)
* for exceptions
*/
static void testExceptions01(CharSequence cs) {
CharSequence nullSeq = null;
// codePointAt
callCodePoint(At, nullSeq, 0, NullPointerException.class);
callCodePoint(At, cs, -1, IndexOutOfBoundsException.class);
callCodePoint(At, cs, cs.length(), IndexOutOfBoundsException.class);
callCodePoint(At, cs, cs.length()*3, IndexOutOfBoundsException.class);
// codePointBefore
callCodePoint(Before, nullSeq, 0, NullPointerException.class);
callCodePoint(Before, cs, -1, IndexOutOfBoundsException.class);
callCodePoint(Before, cs, 0, IndexOutOfBoundsException.class);
callCodePoint(Before, cs, cs.length()+1, IndexOutOfBoundsException.class);
// codePointCount
callCodePointCount(nullSeq, 0, 0, NullPointerException.class);
callCodePointCount(cs, -1, 1, IndexOutOfBoundsException.class);
callCodePointCount(cs, 0, cs.length()+1, IndexOutOfBoundsException.class);
callCodePointCount(cs, 3, 1, IndexOutOfBoundsException.class);
// offsetByCodePoints
callOffsetByCodePoints(nullSeq, 0, 0, NullPointerException.class);
callOffsetByCodePoints(cs, -1, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(cs, cs.length()+1, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(cs, 0, cs.length()*2, IndexOutOfBoundsException.class);
callOffsetByCodePoints(cs, cs.length(), 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(cs, 0, -1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(cs, cs.length(), -cs.length()*2,
IndexOutOfBoundsException.class);
callOffsetByCodePoints(cs, cs.length(), Integer.MIN_VALUE,
IndexOutOfBoundsException.class);
callOffsetByCodePoints(cs, 0, Integer.MAX_VALUE, IndexOutOfBoundsException.class);
}
/**
* Test codePointAt(char[], int)
* codePointAt(char[], int, int)
* codePointBefore(char[], int)
* codePointBefore(char[], int, int)
* codePointCount(char[], int, int)
* offsetByCodePoints(char[], int, int, int, int)
* for exceptions
*/
static void testExceptions02(char[] a) {
char[] nullArray = null;
callCodePoint(At, nullArray, 0, NullPointerException.class);
callCodePoint(At, a, -1, IndexOutOfBoundsException.class);
callCodePoint(At, a, a.length, IndexOutOfBoundsException.class);
callCodePoint(At, a, a.length*3, IndexOutOfBoundsException.class);
callCodePoint(Before, nullArray, 0, NullPointerException.class);
callCodePoint(Before, a, -1, IndexOutOfBoundsException.class);
callCodePoint(Before, a, 0, IndexOutOfBoundsException.class);
callCodePoint(Before, a, a.length+1, IndexOutOfBoundsException.class);
// tests for the methods with limit
callCodePoint(At, nullArray, 0, 1, NullPointerException.class);
callCodePoint(At, a, 0, -1, IndexOutOfBoundsException.class);
callCodePoint(At, a, 0, 0, IndexOutOfBoundsException.class);
callCodePoint(At, a, 0, a.length+1, IndexOutOfBoundsException.class);
callCodePoint(At, a, 2, 1, IndexOutOfBoundsException.class);
callCodePoint(At, a, -1, 1, IndexOutOfBoundsException.class);
callCodePoint(At, a, a.length, 1, IndexOutOfBoundsException.class);
callCodePoint(At, a, a.length*3, 1, IndexOutOfBoundsException.class);
callCodePoint(Before, nullArray, 1, 0, NullPointerException.class);
callCodePoint(Before, a, 2, -1, IndexOutOfBoundsException.class);
callCodePoint(Before, a, 2, 2, IndexOutOfBoundsException.class);
callCodePoint(Before, a, 2, 3, IndexOutOfBoundsException.class);
callCodePoint(Before, a, 2, a.length, IndexOutOfBoundsException.class);
callCodePoint(Before, a, -1, -1, IndexOutOfBoundsException.class);
callCodePoint(Before, a, 0, 0, IndexOutOfBoundsException.class);
callCodePoint(Before, a, a.length+1, a.length-1, IndexOutOfBoundsException.class);
// codePointCount
callCodePointCount(nullArray, 0, 0, NullPointerException.class);
callCodePointCount(a, -1, 1, IndexOutOfBoundsException.class);
callCodePointCount(a, 0, -1, IndexOutOfBoundsException.class);
callCodePointCount(a, 0, a.length+1, IndexOutOfBoundsException.class);
callCodePointCount(a, 1, a.length, IndexOutOfBoundsException.class);
callCodePointCount(a, a.length, 1, IndexOutOfBoundsException.class);
callCodePointCount(a, a.length+1, -1, IndexOutOfBoundsException.class);
// offsetByCodePoints
callOffsetByCodePoints(nullArray, 0, 0, 0, 0, NullPointerException.class);
callOffsetByCodePoints(a, -1, a.length, 1, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length+1, 1, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 10, a.length, 1, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 10, a.length-10, 1, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 10, 10, 21, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 20, -10, 15, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 10, 10, 15, 20, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 10, 10, 15, -20, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length, -1, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length, a.length+1, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length, 0, a.length*2, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length, a.length, 1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length, 0, -1, IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length, a.length, -a.length*2,
IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length, a.length, Integer.MIN_VALUE,
IndexOutOfBoundsException.class);
callOffsetByCodePoints(a, 0, a.length, 0, Integer.MAX_VALUE,
IndexOutOfBoundsException.class);
}
/**
* Test the 1-arg toChars(int) for exceptions
*/
private static void callToChars1(int codePoint, Class expectedException) {
try {
char[] a = Character.toChars(codePoint);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("toChars(int) didn't throw " + expectedException.getName());
}
/**
* Test the 3-arg toChars(int, char[], int) for exceptions
*/
private static void callToChars3(int codePoint, char[] dst, int index,
Class expectedException) {
try {
int n = Character.toChars(codePoint, dst, index);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("toChars(int,char[],int) didn't throw "
+ expectedException.getName());
}
private static void callCodePoint(boolean isAt, CharSequence cs, int index,
Class expectedException) {
try {
int c = isAt ? Character.codePointAt(cs, index)
: Character.codePointBefore(cs, index);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("codePoint" + (isAt ? "At" : "Before")
+ " didn't throw " + expectedException.getName());
}
private static void callCodePoint(boolean isAt, char[] a, int index,
Class expectedException) {
try {
int c = isAt ? Character.codePointAt(a, index)
: Character.codePointBefore(a, index);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("codePoint" + (isAt ? "At" : "Before")
+ " didn't throw " + expectedException.getName());
}
private static void callCodePoint(boolean isAt, char[] a, int index, int limit,
Class<? extends Exception> expectedException) {
try {
int c = isAt ? Character.codePointAt(a, index, limit)
: Character.codePointBefore(a, index, limit);
} catch (Exception e) {
if (expectedException == e.getClass()) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("codePoint" + (isAt ? "At" : "Before")
+ " didn't throw " + expectedException.getName());
}
private static void callCodePointCount(Object data, int beginIndex, int endIndex,
Class expectedException) {
String type = getType(data);
try {
int n = (data instanceof CharSequence) ?
Character.codePointCount((CharSequence) data, beginIndex, endIndex)
: Character.codePointCount((char[]) data, beginIndex, endIndex);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("codePointCount(" + type + "...) didn't throw "
+ expectedException.getName());
}
private static void callOffsetByCodePoints(CharSequence seq, int index, int offset,
Class expectedException) {
try {
int n = Character.offsetByCodePoints(seq, index, offset);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("offsetCodePointCounts(CharSequnce...) didn't throw "
+ expectedException.getName());
}
private static void callOffsetByCodePoints(char[] a, int start, int count,
int index, int offset,
Class expectedException) {
try {
int n = Character.offsetByCodePoints(a, start, count, index, offset);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("offsetCodePointCounts(char[]...) didn't throw "
+ expectedException.getName());
}
private static void callToString(int codePoint, Class expectedException) {
try {
String s = Character.toString(codePoint);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Unspecified exception", e);
}
throw new RuntimeException("toString(int) didn't throw "
+ expectedException.getName());
}
private static String getType(Object data) {
return (data instanceof CharSequence) ? "CharSequence" : "char[]";
}
private static String toHexString(int c) {
return "0x" + Integer.toHexString(c);
}
private static String toHexString(String name, char[] a) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < a.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(name).append('[').append(i).append("]=");
sb.append(toHexString(a[i]));
}
return sb.toString();
}
}

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8303018
* @summary Check j.l.Character.isEmoji/isEmojiPresentation/isEmojiModifier
* isEmojiModifierBase/isEmojiComponent/isExtendedPictographic
* @library /lib/testlibrary/java/lang
*/
import java.io.IOException;
import java.nio.file.Files;
import java.util.AbstractMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static java.lang.Character.MAX_CODE_POINT;
import static java.lang.Character.MIN_CODE_POINT;
import static java.lang.Character.isEmoji;
import static java.lang.Character.isEmojiPresentation;
import static java.lang.Character.isEmojiModifier;
import static java.lang.Character.isEmojiModifierBase;
import static java.lang.Character.isEmojiComponent;
import static java.lang.Character.isExtendedPictographic;
public class TestEmojiProperties {
// Masks representing Emoji properties (16-bit `B` table masks in
// CharacterData.java)
private static final int EMOJI = 0x0040;
private static final int EMOJI_PRESENTATION = 0x0080;
private static final int EMOJI_MODIFIER = 0x0100;
private static final int EMOJI_MODIFIER_BASE = 0x0200;
private static final int EMOJI_COMPONENT = 0x0400;
private static final int EXTENDED_PICTOGRAPHIC = 0x0800;
public static void main(String[] args) throws IOException {
var emojiProps = Files.readAllLines(UCDFiles.EMOJI_DATA).stream()
.map(line -> line.split("#", 2)[0])
.filter(Predicate.not(String::isBlank))
.map(line -> line.split("[ \t]*;[ \t]*", 2))
.flatMap(map -> {
var range = map[0].split("\\.\\.", 2);
var start = Integer.valueOf(range[0], 16);
return range.length == 1 ?
Stream.of(new AbstractMap.SimpleEntry<>(start, convertType(map[1].trim()))) :
IntStream.rangeClosed(start,
Integer.valueOf(range[1], 16))
.mapToObj(cp -> new AbstractMap.SimpleEntry<>(cp, convertType(map[1].trim())));
})
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, (v1, v2) -> v1 | v2));
final var fails = new Integer[1];
fails[0] = 0;
IntStream.rangeClosed(MIN_CODE_POINT, MAX_CODE_POINT).forEach(cp -> {
var props = emojiProps.getOrDefault(cp, 0L);
if ((props & EMOJI) != 0 ^ isEmoji(cp)) {
System.err.printf("""
isEmoji(0x%x) failed. Returned: %b
""", cp, isEmoji(cp));
fails[0] ++;
}
if ((props & EMOJI_PRESENTATION) != 0 ^ isEmojiPresentation(cp)) {
System.err.printf("""
isEmojiPresentation(0x%x) failed. Returned: %b
""", cp, isEmojiPresentation(cp));
fails[0] ++;
}
if ((props & EMOJI_MODIFIER) != 0 ^ isEmojiModifier(cp)) {
System.err.printf("""
isEmojiModifier(0x%x) failed. Returned: %b
""", cp, isEmojiModifier(cp));
fails[0] ++;
}
if ((props & EMOJI_MODIFIER_BASE) != 0 ^ isEmojiModifierBase(cp)) {
System.err.printf("""
isEmojiModifierBase(0x%x) failed. Returned: %b
""", cp, isEmojiModifierBase(cp));
fails[0] ++;
}
if ((props & EMOJI_COMPONENT) != 0 ^ isEmojiComponent(cp)) {
System.err.printf("""
isEmojiComponent(0x%x) failed. Returned: %b
""", cp, isEmojiComponent(cp));
fails[0] ++;
}
if ((props & EXTENDED_PICTOGRAPHIC) != 0 ^ isExtendedPictographic(cp)) {
System.err.printf("""
isExtendedPictographic(0x%x) failed. Returned: %b
""", cp, isExtendedPictographic(cp));
fails[0] ++;
}
});
if (fails[0] != 0) {
throw new RuntimeException("TestEmojiProperties failed=" + fails);
}
}
private static long convertType(String type) {
return switch (type) {
case "Emoji" -> EMOJI;
case "Emoji_Presentation" -> EMOJI_PRESENTATION;
case "Emoji_Modifier" -> EMOJI_MODIFIER;
case "Emoji_Modifier_Base" -> EMOJI_MODIFIER_BASE;
case "Emoji_Component" -> EMOJI_COMPONENT;
case "Extended_Pictographic" -> EXTENDED_PICTOGRAPHIC;
default -> throw new InternalError();
};
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4899380
* @summary Character.is<Property>(int codePoint) methods should return false
* for negative codepoint values. The to<CaseMap> methods should return
* the original codePoint for invalid codePoint ranges.
* @author John O'Conner
*/
public class TestNegativeCodepoint {
public static void main(String[] args) {
int[] invalidCodePoints = { -1, -'a', 0x110000};
for (int x = 0; x < invalidCodePoints.length; ++x) {
int cp = invalidCodePoints[x];
System.out.println("Testing codepoint: " + cp);
// test all of the is<Property> methods
if (Character.isLowerCase(cp) ||
Character.isUpperCase(cp) ||
Character.isTitleCase(cp) ||
Character.isISOControl(cp) ||
Character.isLetterOrDigit(cp) ||
Character.isLetter(cp) ||
Character.isDigit(cp) ||
Character.isDefined(cp) ||
Character.isJavaIdentifierStart(cp) ||
Character.isJavaIdentifierPart(cp) ||
Character.isUnicodeIdentifierStart(cp) ||
Character.isUnicodeIdentifierPart(cp) ||
Character.isIdentifierIgnorable(cp) ||
Character.isSpaceChar(cp) ||
Character.isWhitespace(cp) ||
Character.isMirrored(cp) ||
// test the case mappings
Character.toLowerCase(cp) != cp ||
Character.toUpperCase(cp) != cp ||
Character.toTitleCase(cp) != cp ||
// test directionality of invalid codepoints
Character.getDirectionality(cp) != Character.DIRECTIONALITY_UNDEFINED ||
// test type
Character.getType(cp) != Character.UNASSIGNED ||
// test numeric and digit values
Character.getNumericValue(cp) != -1 ||
Character.digit(cp, 10) != -1 ) {
System.out.println("Failed.");
throw new RuntimeException();
}
// test block value
Character.UnicodeBlock block = null;
try {
block = Character.UnicodeBlock.of(cp);
// if we haven't already thrown an exception because of the illegal
// arguments, then we need to throw one because of an error in the of() method
System.out.println("Failed.");
throw new RuntimeException();
}
catch(IllegalArgumentException e) {
// Since we're testing illegal values, we
// expect to land here every time. If not,
// our test has failed
}
}
System.out.println("Passed.");
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4427146
* @summary Undefined char values should have DIRECTIONALITY_UNDEFINED.
* @author John O'Conner
*/
public class TestUndefinedDirectionality {
public static void main(String[] args) {
int failures = 0;
for (int ch=0x0000;ch <= 0xFFFF; ch++) {
if (!Character.isDefined((char)ch)) {
byte direction = Character.getDirectionality((char)ch);
if (direction != Character.DIRECTIONALITY_UNDEFINED) {
System.err.println("Fail: \\u" + Integer.toString(ch, 16));
failures++;
}
}
}
if (failures != 0) {
throw new RuntimeException("TestUndefinedDirectionality: failed.");
} else {
System.out.println("Passed.");
}
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4453719
* @author John O'Conner
* @summary Undefined char values cannot be Java identifier starts or parts.
*/
public class TestUndefinedIdentifierStartPart {
static int endValue = 0xFFFF;
public static void main(String[] args) {
for (int ch=0x0000; ch <= endValue; ch++) {
if (!Character.isDefined((char)ch) &&
(Character.isJavaIdentifierStart((char)ch) ||
Character.isJavaIdentifierPart((char)ch) ||
Character.isUnicodeIdentifierStart((char)ch) ||
Character.isUnicodeIdentifierPart((char)ch))) {
throw new RuntimeException("Char value " + Integer.toHexString((char)ch));
}
}
System.out.println("Passed");
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4453719
* @author John O'Conner
* @summary Undefined character values should not be ignorable identifiers.
*/
public class TestUndefinedIgnorable {
static int endValue = 0xFFFF;
public static void main(String[] args) {
for (int ch=0x0000; ch <= endValue; ch++) {
if (!Character.isDefined((char)ch) &&
Character.isIdentifierIgnorable((char)ch)) {
throw new RuntimeException("Char value " + Integer.toHexString((char)ch));
}
}
System.out.println("Passed.");
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4453720
* @author John O'Conner
* @summary Undefined char values should not have mirrored property.
*/
public class TestUndefinedMirrored {
static int endValue = 0xFFFF;
public static void main(String[] args) {
for (int ch = 0x0000; ch <= endValue; ch++) {
if (!Character.isDefined((char)ch) && Character.isMirrored((char)ch)) {
throw new RuntimeException("Char value " + Integer.toHexString((char)ch));
}
}
System.out.println("Passed.");
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4453721
* @author John O'Conner
* @summary Unassigned char values should have no numeric properties.
*/
public class TestUndefinedNumeric {
static int endValue = 0xFFFF;
public static void main(String[] args) {
for (int ch = 0x0000; ch <= 0xFFFF; ch++) {
if (!Character.isDefined((char)ch) &&
Character.getNumericValue((char)ch) != -1) {
throw new RuntimeException("Char value " + Integer.toHexString((char)ch));
}
}
System.out.println("Passed.");
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @author John O'Conner
* @bug 4453730
* @summary Undefined character code points do not have titlecase mappings. The
* toTitleCase method should return the argument code value.
*/
public class TestUndefinedTitleCase {
static int endCharValue = 0xFFFF;
public static void main(String[] args) {
for(int ch=0x0000; ch <= endCharValue; ch++) {
if (!Character.isDefined((char)ch) && Character.toTitleCase((char)ch) != (char)ch) {
throw new RuntimeException("Char value " + Integer.toHexString((char)ch));
}
}
System.out.println("Passed");
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4427146
* @summary Some char values that are Unicode spaces are non-breaking. These
* should not be Java whitespaces.
* @author John O'Conner
*/
public class TestWhiteSpace {
public static void main(String[] args) {
// These values should NOT be whitespace
char[] whiteSpace = {'\u00A0', '\u2007', '\u202F'};
for (int x=0;x<whiteSpace.length;x++) {
if (Character.isWhitespace(whiteSpace[x])) {
throw new RuntimeException("Invalid whitespace: \\u" +
Integer.toString((int)whiteSpace[x], 16));
}
}
System.out.println("Passed.");
}
}

View file

@ -0,0 +1,346 @@
/*
* Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4830803 4886934 6565620 6959267 7070436 7198195 8032446 8072600 8202771
* 8221431
* @summary Check that the UnicodeBlock forName() method works as expected
* and block ranges are correct for all Unicode characters.
* @library /lib/testlibrary/java/lang
* @run main CheckBlocks
* @author John O'Conner
*/
import java.lang.Character.UnicodeBlock;
import java.lang.reflect.Field;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.HashSet;
import java.util.Locale;
public class CheckBlocks {
static boolean err = false;
static Class<?> clazzUnicodeBlock;
public static void main(String[] args) throws Exception {
generateBlockList();
try {
clazzUnicodeBlock = Class.forName("java.lang.Character$UnicodeBlock");
} catch (ClassNotFoundException e) {
throw new RuntimeException("Class.forName(\"java.lang.Character$UnicodeBlock\") failed.");
}
for (Block blk : blocks) {
test4830803_1(blk);
test4830803_2();
test4886934(blk);
}
test8202771();
if (err) {
throw new RuntimeException("Failed");
} else {
System.out.println("Passed");
}
}
/**
* Check that the UnicodeBlock forName() method works as expected.
*/
private static void test4830803_1(Block blk) throws Exception {
/*
* Try 3 forms of block name in the forName() method. Each form should
* produce the same expected block.
*/
String blkName = blk.getName();
// For backward compatibility
switch (blkName) {
case "COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS":
blkName = "COMBINING_MARKS_FOR_SYMBOLS";
System.out.println("*** COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS"
+ " is replaced with COMBINING_MARKS_FOR_SYMBOLS"
+ " for backward compatibility.");
break;
case "GREEK_AND_COPTIC":
blkName = "GREEK";
System.out.println("*** GREEK_AND_COPTIC is replaced with GREEK"
+ " for backward compatibility.");
break;
case "CYRILLIC_SUPPLEMENT":
blkName = "CYRILLIC_SUPPLEMENTARY";
System.out.println("*** CYRILLIC_SUPPLEMENT is replaced with"
+ " CYRILLIC_SUPPLEMENTARY for backward compatibility.");
break;
default:
break;
}
String expectedBlock = null;
try {
expectedBlock = clazzUnicodeBlock.getField(blkName).getName();
} catch (NoSuchFieldException | SecurityException e) {
System.err.println("Error: " + blkName + " was not found.");
err = true;
return;
}
String canonicalBlockName = blk.getOriginalName();
String idBlockName = expectedBlock;
String regexBlockName = toRegExString(canonicalBlockName);
if (regexBlockName == null) {
System.err.println("Error: Block name which was processed with regex was null.");
err = true;
return;
}
if (!expectedBlock.equals(UnicodeBlock.forName(canonicalBlockName).toString())) {
System.err.println("Error #1: UnicodeBlock.forName(\"" +
canonicalBlockName + "\") returned wrong value.\n\tGot: " +
UnicodeBlock.forName(canonicalBlockName) +
"\n\tExpected: " + expectedBlock);
err = true;
}
if (!expectedBlock.equals(UnicodeBlock.forName(idBlockName).toString())) {
System.err.println("Error #2: UnicodeBlock.forName(\"" +
idBlockName + "\") returned wrong value.\n\tGot: " +
UnicodeBlock.forName(idBlockName) +
"\n\tExpected: " + expectedBlock);
err = true;
}
if (!expectedBlock.equals(UnicodeBlock.forName(regexBlockName).toString())) {
System.err.println("Error #3: UnicodeBlock.forName(\"" +
regexBlockName + "\") returned wrong value.\n\tGot: " +
UnicodeBlock.forName(regexBlockName) +
"\n\tExpected: " + expectedBlock);
err = true;
}
}
/**
* now try a bad block name. This should produce an IAE.
*/
private static void test4830803_2() {
boolean threwExpected = false;
try {
UnicodeBlock block = UnicodeBlock.forName("notdefined");
}
catch(IllegalArgumentException e) {
threwExpected = true;
}
if (threwExpected == false) {
System.err.println("Error: UnicodeBlock.forName(\"notdefined\") should throw IllegalArgumentException.");
err = true;
}
}
/**
* Convert the argument to a block name form used by the regex package.
* That is, remove all spaces.
*/
private static String toRegExString(String str) {
String[] tokens = null;
StringBuilder retStr = new StringBuilder();
try {
tokens = str.split(" ");
}
catch(java.util.regex.PatternSyntaxException e) {
return null;
}
for(int x=0; x < tokens.length; ++x) {
retStr.append(tokens[x]);
}
return retStr.toString();
}
private static void test4886934(Block blk) {
String blkName = blk.getName();
String blkOrigName = blk.getOriginalName();
UnicodeBlock block;
String blockName;
// For backward compatibility
switch (blkName) {
case "COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS":
blkName = "COMBINING_MARKS_FOR_SYMBOLS";
System.out.println("*** COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS"
+ " is replaced with COMBINING_MARKS_FOR_SYMBOLS"
+ " for backward compatibility.");
break;
case "GREEK_AND_COPTIC":
blkName = "GREEK";
System.out.println("*** GREEK_AND_COPTIC is replaced with GREEK"
+ " for backward compatibility.");
break;
case "CYRILLIC_SUPPLEMENT":
blkName = "CYRILLIC_SUPPLEMENTARY";
System.out.println("*** CYRILLIC_SUPPLEMENT is replaced with"
+ " CYRILLIC_SUPPLEMENTARY for backward compatibility.");
break;
default:
break;
}
for (int ch = blk.getBegin(); ch <= blk.getEnd(); ch++) {
block = UnicodeBlock.of(ch);
if (block == null) {
System.err.println("Error: The block for " + blkName
+ " is missing. Please check java.lang.Character.UnicodeBlock.");
err = true;
break;
}
blockName = block.toString();
if (!blockName.equals(blkName)) {
System.err.println("Error: Character(0x"
+ Integer.toHexString(ch).toUpperCase()
+ ") should be in \"" + blkName + "\" block "
+ "(Block name is \"" + blkOrigName + "\")"
+ " but found in \"" + blockName + "\" block.");
err = true;
}
}
}
/**
* Check if every Field of Character.UnicodeBlock is a valid Unicode Block.
*/
private static void test8202771() {
Field[] fields = clazzUnicodeBlock.getFields();
for (Field f : fields) {
// Handle Deprecated field "SURROGATES_AREA".
if (f.getAnnotation(Deprecated.class) != null) {
continue;
}
String blkName = f.getName();
switch (blkName) {
case "COMBINING_MARKS_FOR_SYMBOLS":
validateBlock("COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS");
break;
case "GREEK":
validateBlock("GREEK_AND_COPTIC");
break;
case "CYRILLIC_SUPPLEMENTARY":
validateBlock("CYRILLIC_SUPPLEMENT");
break;
default:
validateBlock(blkName);
break;
}
}
}
private static void validateBlock(String blkName) {
for (Block block : blocks) {
String blockName = block.getName();
if (blockName.equals(blkName)) {
return;
}
}
err = true;
System.err.println(blkName + " is not a valid Unicode Block.");
}
// List of all Unicode blocks, their start, and end codepoints.
public static HashSet<Block> blocks = new HashSet<>();
private static void generateBlockList() throws Exception {
File blockData = UCDFiles.BLOCKS.toFile();
try (BufferedReader f = new BufferedReader(new FileReader(blockData))) {
String line;
while ((line = f.readLine()) != null) {
if (line.length() == 0 || line.charAt(0) == '#') {
continue;
}
int index1 = line.indexOf('.');
int begin = Integer.parseInt(line.substring(0, index1), 16);
int index2 = line.indexOf(';');
int end = Integer.parseInt(line.substring(index1 + 2, index2), 16);
String name = line.substring(index2 + 1).trim();
System.out.println(" Adding a Block(" + Integer.toHexString(begin) + ", " + Integer.toHexString(end)
+ ", " + name + ")");
blocks.add(new Block(begin, end, name));
}
}
}
}
class Block {
public Block() {
blockBegin = 0;
blockEnd = 0;
blockName = null;
}
public Block(int begin, int end, String name) {
blockBegin = begin;
blockEnd = end;
blockName = name.replaceAll("[ -]", "_").toUpperCase(Locale.ENGLISH);
originalBlockName = name;
}
public int getBegin() {
return blockBegin;
}
public int getEnd() {
return blockEnd;
}
public String getName() {
return blockName;
}
public String getOriginalName() {
return originalBlockName;
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof Block)) return false;
Block other = (Block)obj;
return other.blockBegin == blockBegin &&
other.blockEnd == blockEnd &&
other.blockName.equals(blockName) &&
other.originalBlockName.equals(originalBlockName);
}
int blockBegin, blockEnd;
String blockName, originalBlockName;
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8080535 8191410 8215194 8221431 8239383 8268081 8283465 8284856
* @summary Check if the NUM_ENTITIES field reflects the correct number
* of Character.UnicodeBlock constants. Also checks the size of
* Character.UnicodeScript's "aliases" map.
* @modules java.base/java.lang:open
* @run junit NumberEntities
*/
import java.lang.reflect.Field;
import java.util.Map;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class NumberEntities {
@Test
public void test_UnicodeBlock_NumberEntities() throws Throwable {
// The number of entries in Character.UnicodeBlock.map.
// See src/java.base/share/classes/java/lang/Character.java
Field n = Character.UnicodeBlock.class.getDeclaredField("NUM_ENTITIES");
Field m = Character.UnicodeBlock.class.getDeclaredField("map");
n.setAccessible(true);
m.setAccessible(true);
assertEquals(n.getInt(null), ((Map)m.get(null)).size());
}
@Test
public void test_UnicodeScript_aliases() throws Throwable {
// The number of entries in Character.UnicodeScript.aliases.
// See src/java.base/share/classes/java/lang/Character.java
Field aliases = Character.UnicodeScript.class.getDeclaredField("aliases");
aliases.setAccessible(true);
assertEquals(Character.UnicodeScript.UNKNOWN.ordinal() + 1, ((Map)aliases.get(null)).size());
}
}

View file

@ -0,0 +1,175 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4397357 6565620 6959267 8032446 8072600 8221431
* @summary Confirm normal case mappings are handled correctly.
* @library /lib/testlibrary/java/lang
* @run main/timeout=200 UnicodeCasingTest
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class UnicodeCasingTest {
private static boolean err = false;
// Locales which are used for testing
private static List<Locale> locales = new ArrayList<>();
static {
locales.add(Locale.of("az"));
locales.addAll(java.util.Arrays.asList(Locale.getAvailableLocales()));
}
public static void main(String[] args) {
UnicodeCasingTest specialCasingTest = new UnicodeCasingTest();
specialCasingTest.test();
}
private void test() {
Locale defaultLocale = Locale.getDefault();
BufferedReader in = null;
try {
File file = UCDFiles.UNICODE_DATA.toFile();
int locale_num = locales.size();
for (int l = 0; l < locale_num; l++) {
Locale locale = locales.get(l);
Locale.setDefault(locale);
System.out.println("Testing on " + locale + " locale....");
in = new BufferedReader(new FileReader(file));
String line;
while ((line = in.readLine()) != null) {
if (line.length() == 0 || line.charAt(0) == '#') {
continue;
}
test(line);
}
in.close();
in = null;
}
}
catch (Exception e) {
err = true;
e.printStackTrace();
}
finally {
if (in != null) {
try {
in.close();
}
catch (Exception e) {
}
}
Locale.setDefault(defaultLocale);
if (err) {
throw new RuntimeException("UnicodeCasingTest failed.");
} else {
System.out.println("UnicodeCasingTest passed.");
}
}
}
private void test(String line) {
String[] fields = line.split(";", 15);
int orig = convert(fields[0]);
if (fields[12].length() != 0) {
testUpperCase(orig, convert(fields[12]));
} else {
testUpperCase(orig, orig);
}
if (fields[13].length() != 0) {
testLowerCase(orig, convert(fields[13]));
} else {
testLowerCase(orig, orig);
}
if (fields[14].length() != 0) {
testTitleCase(orig, convert(fields[14]));
} else {
testTitleCase(orig, orig);
}
}
private void testUpperCase(int orig, int expected) {
int got = Character.toUpperCase(orig);
if (expected != got) {
err = true;
System.err.println("toUpperCase(" +
") failed.\n\tOriginal: " + toString(orig) +
"\n\tGot: " + toString(got) +
"\n\tExpected: " + toString(expected));
}
}
private void testLowerCase(int orig, int expected) {
int got = Character.toLowerCase(orig);
if (expected != got) {
err = true;
System.err.println("toLowerCase(" +
") failed.\n\tOriginal: " + toString(orig) +
"\n\tGot: " + toString(got) +
"\n\tExpected: " + toString(expected));
}
}
private void testTitleCase(int orig, int expected) {
int got = Character.toTitleCase(orig);
if (expected != got) {
err = true;
System.err.println("toTitleCase(" +
") failed.\n\tOriginal: " + toString(orig) +
"\n\tGot: " + toString(got) +
"\n\tExpected: " + toString(expected));
}
}
private int convert(String str) {
return Integer.parseInt(str, 16);
}
private String toString(int i) {
return Integer.toHexString(i).toUpperCase();
}
}

View file

@ -0,0 +1,754 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.File;
import java.util.regex.Pattern;
import java.util.ArrayList;
/**
* The UnicodeSpec class provides a way to read in Unicode character
* properties from a Unicode data file. One instance of class UnicodeSpec
* holds a decoded version of one line of the data file. The file may
* be obtained from www.unicode.org. The method readSpecFile returns an array
* of UnicodeSpec objects.
*
* @author Guy Steele
* @author John O'Conner
*/
public class UnicodeSpec {
public UnicodeSpec() {
this(0xffff);
}
public UnicodeSpec(int codePoint) {
this.codePoint = codePoint;
generalCategory = UNASSIGNED;
bidiCategory = DIRECTIONALITY_UNDEFINED;
mirrored = false;
titleMap = 0xFFFF;
upperMap = 0xFFFF;
lowerMap = 0xFFFF;
decimalValue = -1;
digitValue = -1;
numericValue = "";
oldName = null;
comment = null;
name = null;
}
public String toString() {
StringBuffer result = new StringBuffer(hex6(codePoint));
if (getUpperMap() != 0xffff) {
result.append(", upper=").append(hex6(upperMap));
}
if (getLowerMap() != 0xffff) {
result.append(", lower=").append(hex6(lowerMap));
}
if (getTitleMap() != 0xffff) {
result.append(", title=").append(hex6(titleMap));
}
return result.toString();
}
static String hex4(int n) {
String q = Long.toHexString(n & 0xFFFF).toUpperCase();
return "0000".substring(Math.min(4, q.length())) + q;
}
static String hex6(int n) {
String str = Integer.toHexString(n & 0xFFFFFF).toUpperCase();
return "000000".substring(Math.min(6, str.length())) + str;
}
/**
* Given one line of a Unicode data file as a String, parse the line
* and return a UnicodeSpec object that contains the same character information.
*
* @param s a line of the Unicode data file to be parsed
* @return a UnicodeSpec object, or null if the parsing process failed for some reason
*/
public static UnicodeSpec parse(String s) {
UnicodeSpec spec = null;
String[] tokens = null;
try {
tokens = tokenSeparator.split(s, REQUIRED_FIELDS);
spec = new UnicodeSpec();
spec.setCodePoint(parseCodePoint(tokens[FIELD_VALUE]));
spec.setName(parseName(tokens[FIELD_NAME]));
spec.setGeneralCategory(parseGeneralCategory(tokens[FIELD_CATEGORY]));
spec.setBidiCategory(parseBidiCategory(tokens[FIELD_BIDI]));
spec.setCombiningClass(parseCombiningClass(tokens[FIELD_CLASS]));
spec.setDecomposition(parseDecomposition(tokens[FIELD_DECOMPOSITION]));
spec.setDecimalValue(parseDecimalValue(tokens[FIELD_DECIMAL]));
spec.setDigitValue(parseDigitValue(tokens[FIELD_DIGIT]));
spec.setNumericValue(parseNumericValue(tokens[FIELD_NUMERIC]));
spec.setMirrored(parseMirrored(tokens[FIELD_MIRRORED]));
spec.setOldName(parseOldName(tokens[FIELD_OLDNAME]));
spec.setComment(parseComment(tokens[FIELD_COMMENT]));
spec.setUpperMap(parseUpperMap(tokens[FIELD_UPPERCASE]));
spec.setLowerMap(parseLowerMap(tokens[FIELD_LOWERCASE]));
spec.setTitleMap(parseTitleMap(tokens[FIELD_TITLECASE]));
}
catch(Exception e) {
spec = null;
System.out.println("Error parsing spec line.");
}
return spec;
}
/**
* Parse the codePoint attribute for a Unicode character. If the parse succeeds,
* the codePoint field of this UnicodeSpec object is updated and false is returned.
*
* The codePoint attribute should be a four-digit hexadecimal integer.
*
* @param s the codePoint attribute extracted from a line of the Unicode data file
* @return code point if successful
* @exception NumberFormatException if unable to parse argument
*/
public static int parseCodePoint(String s) throws NumberFormatException {
return Integer.parseInt(s, 16);
}
public static String parseName(String s) throws Exception {
if (s==null) throw new Exception("Cannot parse name.");
return s;
}
public static byte parseGeneralCategory(String s) throws Exception {
byte category = GENERAL_CATEGORY_COUNT;
for (byte x=0; x<generalCategoryList.length; x++) {
if (s.equals(generalCategoryList[x][SHORT])) {
category = x;
break;
}
}
if (category >= GENERAL_CATEGORY_COUNT) {
throw new Exception("Could not parse general category.");
}
return category;
}
public static byte parseBidiCategory(String s) throws Exception {
byte category = DIRECTIONALITY_CATEGORY_COUNT;
for (byte x=0; x<bidiCategoryList.length; x++) {
if (s.equals(bidiCategoryList[x][SHORT])) {
category = x;
break;
}
}
if (category >= DIRECTIONALITY_CATEGORY_COUNT) {
throw new Exception("Could not parse bidi category.");
}
return category;
}
/**
* Parse the combining attribute for a Unicode character. If there is a combining
* attribute and the parse succeeds, then the hasCombining field is set to true,
* the combining field of this UnicodeSpec object is updated, and false is returned.
* If the combining attribute is an empty string, the parse succeeds but the
* hasCombining field is set to false. (and false is returned).
*
* The combining attribute, if any, should be a nonnegative decimal integer.
*
* @param s the combining attribute extracted from a line of the Unicode data file
* @return the combining class value if any, -1 if property not defined
* @exception Exception if can't parse the combining class
*/
public static int parseCombiningClass(String s) throws Exception {
int combining = -1;
if (s.length()>0) {
combining = Integer.parseInt(s, 10);
}
return combining;
}
/**
* Parse the decomposition attribute for a Unicode character. If the parse succeeds,
* the decomposition field of this UnicodeSpec object is updated and false is returned.
*
* The decomposition attribute is complicated; for now, it is treated as a string.
*
* @param s the decomposition attribute extracted from a line of the Unicode data file
* @return true if the parse failed; otherwise false
*/
public static String parseDecomposition(String s) throws Exception {
if (s==null) throw new Exception("Cannot parse decomposition.");
return s;
}
/**
* Parse the decimal value attribute for a Unicode character. If there is a decimal value
* attribute and the parse succeeds, then the hasDecimalValue field is set to true,
* the decimalValue field of this UnicodeSpec object is updated, and false is returned.
* If the decimal value attribute is an empty string, the parse succeeds but the
* hasDecimalValue field is set to false. (and false is returned).
*
* The decimal value attribute, if any, should be a nonnegative decimal integer.
*
* @param s the decimal value attribute extracted from a line of the Unicode data file
* @return the decimal value as an int, -1 if no decimal value defined
* @exception NumberFormatException if the parse fails
*/
public static int parseDecimalValue(String s) throws NumberFormatException {
int value = -1;
if (s.length() > 0) {
value = Integer.parseInt(s, 10);
}
return value;
}
/**
* Parse the digit value attribute for a Unicode character. If there is a digit value
* attribute and the parse succeeds, then the hasDigitValue field is set to true,
* the digitValue field of this UnicodeSpec object is updated, and false is returned.
* If the digit value attribute is an empty string, the parse succeeds but the
* hasDigitValue field is set to false. (and false is returned).
*
* The digit value attribute, if any, should be a nonnegative decimal integer.
*
* @param s the digit value attribute extracted from a line of the Unicode data file
* @return the digit value as an non-negative int, or -1 if no digit property defined
* @exception NumberFormatException if the parse fails
*/
public static int parseDigitValue(String s) throws NumberFormatException {
int value = -1;
if (s.length() > 0) {
value = Integer.parseInt(s, 10);
}
return value;
}
public static String parseNumericValue(String s) throws Exception {
if (s == null) throw new Exception("Cannot parse numeric value.");
return s;
}
public static String parseComment(String s) throws Exception {
if (s == null) throw new Exception("Cannot parse comment.");
return s;
}
public static boolean parseMirrored(String s) throws Exception {
boolean mirrored;
if (s.length() == 1) {
if (s.charAt(0) == 'Y') {mirrored = true;}
else if (s.charAt(0) == 'N') {mirrored = false;}
else {throw new Exception("Cannot parse mirrored property.");}
}
else { throw new Exception("Cannot parse mirrored property.");}
return mirrored;
}
public static String parseOldName(String s) throws Exception {
if (s == null) throw new Exception("Cannot parse old name");
return s;
}
/**
* Parse the uppercase mapping attribute for a Unicode character. If there is a uppercase
* mapping attribute and the parse succeeds, then the hasUpperMap field is set to true,
* the upperMap field of this UnicodeSpec object is updated, and false is returned.
* If the uppercase mapping attribute is an empty string, the parse succeeds but the
* hasUpperMap field is set to false. (and false is returned).
*
* The uppercase mapping attribute should be a four-digit hexadecimal integer.
*
* @param s the uppercase mapping attribute extracted from a line of the Unicode data file
* @return uppercase char if defined, \uffff otherwise
* @exception NumberFormatException if parse fails
*/
public static int parseUpperMap(String s) throws NumberFormatException {
int upperCase = 0xFFFF;
if (s.length() >= 4) {
upperCase = Integer.parseInt(s, 16);
}
else if (s.length() != 0) {
throw new NumberFormatException();
}
return upperCase;
}
/**
* Parse the lowercase mapping attribute for a Unicode character. If there is a lowercase
* mapping attribute and the parse succeeds, then the hasLowerMap field is set to true,
* the lowerMap field of this UnicodeSpec object is updated, and false is returned.
* If the lowercase mapping attribute is an empty string, the parse succeeds but the
* hasLowerMap field is set to false. (and false is returned).
*
* The lowercase mapping attribute should be a four-digit hexadecimal integer.
*
* @param s the lowercase mapping attribute extracted from a line of the Unicode data file
* @return lowercase char mapping if defined, \uFFFF otherwise
* @exception NumberFormatException if parse fails
*/
public static int parseLowerMap(String s) throws NumberFormatException {
int lowerCase = 0xFFFF;
if (s.length() >= 4) {
lowerCase = Integer.parseInt(s, 16);
}
else if (s.length() != 0) {
throw new NumberFormatException();
}
return lowerCase;
}
/**
* Parse the titlecase mapping attribute for a Unicode character. If there is a titlecase
* mapping attribute and the parse succeeds, then the hasTitleMap field is set to true,
* the titleMap field of this UnicodeSpec object is updated, and false is returned.
* If the titlecase mapping attribute is an empty string, the parse succeeds but the
* hasTitleMap field is set to false. (and false is returned).
*
* The titlecase mapping attribute should be a four-digit hexadecimal integer.
*
* @param s the titlecase mapping attribute extracted from a line of the Unicode data file
* @return title case char mapping if defined, \uFFFF otherwise
* @exception NumberFormatException if parse fails
*/
public static int parseTitleMap(String s) throws NumberFormatException {
int titleCase = 0xFFFF;
if (s.length() >= 4) {
titleCase = Integer.parseInt(s, 16);
}
else if (s.length() != 0) {
throw new NumberFormatException();
}
return titleCase;
}
/**
* Read and parse a Unicode data file.
*
* @param file a file specifying the Unicode data file to be read
* @return an array of UnicodeSpec objects, one for each line of the
* Unicode data file that could be successfully parsed as
* specifying Unicode character attributes
*/
public static UnicodeSpec[] readSpecFile(File file, int plane) throws FileNotFoundException {
ArrayList<UnicodeSpec> list = new ArrayList<>(3000);
UnicodeSpec[] result = null;
int count = 0;
BufferedReader f = new BufferedReader(new FileReader(file));
String line = null;
loop:
while(true) {
try {
line = f.readLine();
}
catch (IOException e) {
break loop;
}
if (line == null) break loop;
UnicodeSpec item = parse(line.trim());
int specPlane = item.getCodePoint() >>> 16;
if (specPlane < plane) continue;
if (specPlane > plane) break;
if (item != null) {
list.add(item);
}
}
result = new UnicodeSpec[list.size()];
list.toArray(result);
return result;
}
void setCodePoint(int value) {
codePoint = value;
}
/**
* Return the code point in this Unicode specification
* @return the char code point representing by the specification
*/
public int getCodePoint() {
return codePoint;
}
void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
void setGeneralCategory(byte category) {
generalCategory = category;
}
public byte getGeneralCategory() {
return generalCategory;
}
void setBidiCategory(byte category) {
bidiCategory = category;
}
public byte getBidiCategory() {
return bidiCategory;
}
void setCombiningClass(int combiningClass) {
this.combiningClass = combiningClass;
}
public int getCombiningClass() {
return combiningClass;
}
void setDecomposition(String decomposition) {
this.decomposition = decomposition;
}
public String getDecomposition() {
return decomposition;
}
void setDecimalValue(int value) {
decimalValue = value;
}
public int getDecimalValue() {
return decimalValue;
}
public boolean isDecimalValue() {
return decimalValue != -1;
}
void setDigitValue(int value) {
digitValue = value;
}
public int getDigitValue() {
return digitValue;
}
public boolean isDigitValue() {
return digitValue != -1;
}
void setNumericValue(String value) {
numericValue = value;
}
public String getNumericValue() {
return numericValue;
}
public boolean isNumericValue() {
return numericValue.length() > 0;
}
void setMirrored(boolean value) {
mirrored = value;
}
public boolean isMirrored() {
return mirrored;
}
void setOldName(String name) {
oldName = name;
}
public String getOldName() {
return oldName;
}
void setComment(String comment) {
this.comment = comment;
}
public String getComment() {
return comment;
}
void setUpperMap(int ch) {
upperMap = ch;
};
public int getUpperMap() {
return upperMap;
}
public boolean hasUpperMap() {
return upperMap != 0xffff;
}
void setLowerMap(int ch) {
lowerMap = ch;
}
public int getLowerMap() {
return lowerMap;
}
public boolean hasLowerMap() {
return lowerMap != 0xffff;
}
void setTitleMap(int ch) {
titleMap = ch;
}
public int getTitleMap() {
return titleMap;
}
public boolean hasTitleMap() {
return titleMap != 0xffff;
}
int codePoint; // the characters UTF-32 code value
String name; // the ASCII name
byte generalCategory; // general category, available via Characte.getType()
byte bidiCategory; // available via Character.getBidiType()
int combiningClass; // not used in Character
String decomposition; // not used in Character
int decimalValue; // decimal digit value
int digitValue; // not all digits are decimal
String numericValue; // numeric value if digit or non-digit
boolean mirrored; //
String oldName;
String comment;
int upperMap;
int lowerMap;
int titleMap;
// this is the number of fields in one line of the UnicodeData.txt file
// each field is separated by a semicolon (a token)
static final int REQUIRED_FIELDS = 15;
/**
* General category types
* To preserve compatibility, these values cannot be changed
*/
public static final byte
UNASSIGNED = 0, // Cn normative
UPPERCASE_LETTER = 1, // Lu normative
LOWERCASE_LETTER = 2, // Ll normative
TITLECASE_LETTER = 3, // Lt normative
MODIFIER_LETTER = 4, // Lm normative
OTHER_LETTER = 5, // Lo normative
NON_SPACING_MARK = 6, // Mn informative
ENCLOSING_MARK = 7, // Me informative
COMBINING_SPACING_MARK = 8, // Mc normative
DECIMAL_DIGIT_NUMBER = 9, // Nd normative
LETTER_NUMBER = 10, // Nl normative
OTHER_NUMBER = 11, // No normative
SPACE_SEPARATOR = 12, // Zs normative
LINE_SEPARATOR = 13, // Zl normative
PARAGRAPH_SEPARATOR = 14, // Zp normative
CONTROL = 15, // Cc normative
FORMAT = 16, // Cf normative
// 17 is unused for no apparent reason,
// but must preserve forward compatibility
PRIVATE_USE = 18, // Co normative
SURROGATE = 19, // Cs normative
DASH_PUNCTUATION = 20, // Pd informative
START_PUNCTUATION = 21, // Ps informative
END_PUNCTUATION = 22, // Pe informative
CONNECTOR_PUNCTUATION = 23, // Pc informative
OTHER_PUNCTUATION = 24, // Po informative
MATH_SYMBOL = 25, // Sm informative
CURRENCY_SYMBOL = 26, // Sc informative
MODIFIER_SYMBOL = 27, // Sk informative
OTHER_SYMBOL = 28, // So informative
INITIAL_QUOTE_PUNCTUATION = 29, // Pi informative
FINAL_QUOTE_PUNCTUATION = 30, // Pf informative
// this value is only used in the character generation tool
// it can change to accommodate the addition of new categories.
GENERAL_CATEGORY_COUNT = 31; // sentinel value
static final byte SHORT = 0, LONG = 1;
// general category type strings
// NOTE: The order of this category array is dependent on the assignment of
// category constants above. We want to access this array using constants above.
// [][SHORT] is the SHORT name, [][LONG] is the LONG name
static final String[][] generalCategoryList = {
{"Cn", "UNASSIGNED"},
{"Lu", "UPPERCASE_LETTER"},
{"Ll", "LOWERCASE_LETTER"},
{"Lt", "TITLECASE_LETTER"},
{"Lm", "MODIFIER_LETTER"},
{"Lo", "OTHER_LETTER"},
{"Mn", "NON_SPACING_MARK"},
{"Me", "ENCLOSING_MARK"},
{"Mc", "COMBINING_SPACING_MARK"},
{"Nd", "DECIMAL_DIGIT_NUMBER"},
{"Nl", "LETTER_NUMBER"},
{"No", "OTHER_NUMBER"},
{"Zs", "SPACE_SEPARATOR"},
{"Zl", "LINE_SEPARATOR"},
{"Zp", "PARAGRAPH_SEPARATOR"},
{"Cc", "CONTROL"},
{"Cf", "FORMAT"},
{"xx", "unused"},
{"Co", "PRIVATE_USE"},
{"Cs", "SURROGATE"},
{"Pd", "DASH_PUNCTUATION"},
{"Ps", "START_PUNCTUATION"},
{"Pe", "END_PUNCTUATION"},
{"Pc", "CONNECTOR_PUNCTUATION"},
{"Po", "OTHER_PUNCTUATION"},
{"Sm", "MATH_SYMBOL"},
{"Sc", "CURRENCY_SYMBOL"},
{"Sk", "MODIFIER_SYMBOL"},
{"So", "OTHER_SYMBOL"},
{"Pi", "INITIAL_QUOTE_PUNCTUATION"},
{"Pf", "FINAL_QUOTE_PUNCTUATION"}
};
/**
* Bidirectional categories
*/
public static final byte
DIRECTIONALITY_UNDEFINED = -1,
// Strong category
DIRECTIONALITY_LEFT_TO_RIGHT = 0, // L
DIRECTIONALITY_RIGHT_TO_LEFT = 1, // R
DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = 2, // AL
// Weak category
DIRECTIONALITY_EUROPEAN_NUMBER = 3, // EN
DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = 4, // ES
DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = 5, // ET
DIRECTIONALITY_ARABIC_NUMBER = 6, // AN
DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = 7, // CS
DIRECTIONALITY_NONSPACING_MARK = 8, // NSM
DIRECTIONALITY_BOUNDARY_NEUTRAL = 9, // BN
// Neutral category
DIRECTIONALITY_PARAGRAPH_SEPARATOR = 10, // B
DIRECTIONALITY_SEGMENT_SEPARATOR = 11, // S
DIRECTIONALITY_WHITESPACE = 12, // WS
DIRECTIONALITY_OTHER_NEUTRALS = 13, // ON
DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = 14, // LRE
DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = 15, // LRO
DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = 16, // RLE
DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = 17, // RLO
DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18, // PDF
DIRECTIONALITY_LEFT_TO_RIGHT_ISOLATE = 19, // LRI
DIRECTIONALITY_RIGHT_TO_LEFT_ISOLATE = 20, // RLI
DIRECTIONALITY_FIRST_STRONG_ISOLATE = 21, // FSI
DIRECTIONALITY_POP_DIRECTIONAL_ISOLATE = 22, // PDI
DIRECTIONALITY_CATEGORY_COUNT = 23; // sentinel value
// If changes are made to the above bidi category assignments, this
// list of bidi category names must be changed to keep their order in synch.
// Access this list using the bidi category constants above.
static final String[][] bidiCategoryList = {
{"L", "DIRECTIONALITY_LEFT_TO_RIGHT"},
{"R", "DIRECTIONALITY_RIGHT_TO_LEFT"},
{"AL", "DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC"},
{"EN", "DIRECTIONALITY_EUROPEAN_NUMBER"},
{"ES", "DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR"},
{"ET", "DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR"},
{"AN", "DIRECTIONALITY_ARABIC_NUMBER"},
{"CS", "DIRECTIONALITY_COMMON_NUMBER_SEPARATOR"},
{"NSM", "DIRECTIONALITY_NONSPACING_MARK"},
{"BN", "DIRECTIONALITY_BOUNDARY_NEUTRAL"},
{"B", "DIRECTIONALITY_PARAGRAPH_SEPARATOR"},
{"S", "DIRECTIONALITY_SEGMENT_SEPARATOR"},
{"WS", "DIRECTIONALITY_WHITESPACE"},
{"ON", "DIRECTIONALITY_OTHER_NEUTRALS"},
{"LRE", "DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING"},
{"LRO", "DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE"},
{"RLE", "DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING"},
{"RLO", "DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE"},
{"PDF", "DIRECTIONALITY_POP_DIRECTIONAL_FORMAT"},
{"LRI", "DIRECTIONALITY_LEFT_TO_RIGHT_ISOLATE"},
{"RLI", "DIRECTIONALITY_RIGHT_TO_LEFT_ISOLATE"},
{"FSI", "DIRECTIONALITY_FIRST_STRONG_ISOLATE"},
{"PDI", "DIRECTIONALITY_POP_DIRECTIONAL_ISOLATE"},
};
// Unicode specification lines have fields in this order.
static final byte
FIELD_VALUE = 0,
FIELD_NAME = 1,
FIELD_CATEGORY = 2,
FIELD_CLASS = 3,
FIELD_BIDI = 4,
FIELD_DECOMPOSITION = 5,
FIELD_DECIMAL = 6,
FIELD_DIGIT = 7,
FIELD_NUMERIC = 8,
FIELD_MIRRORED = 9,
FIELD_OLDNAME = 10,
FIELD_COMMENT = 11,
FIELD_UPPERCASE = 12,
FIELD_LOWERCASE = 13,
FIELD_TITLECASE = 14;
static final Pattern tokenSeparator = Pattern.compile(";");
public static void main(String[] args) {
UnicodeSpec[] spec = null;
if (args.length == 2 ) {
try {
File file = new File(args[0]);
int plane = Integer.parseInt(args[1]);
spec = UnicodeSpec.readSpecFile(file, plane);
System.out.println("UnicodeSpec[" + spec.length + "]:");
for (int x=0; x<spec.length; x++) {
System.out.println(spec[x].toString());
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}