--- old/src/share/classes/java/lang/String.java 2014-01-21 13:47:47.000000000 -0800 +++ new/src/share/classes/java/lang/String.java 2014-01-21 13:47:47.000000000 -0800 @@ -2549,87 +2549,88 @@ if (locale == null) { throw new NullPointerException(); } - int firstUpper; final int len = value.length; - /* Now check if there are any characters that need to be changed. */ - scan: { - for (firstUpper = 0 ; firstUpper < len; ) { - char c = value[firstUpper]; - if ((c >= Character.MIN_HIGH_SURROGATE) - && (c <= Character.MAX_HIGH_SURROGATE)) { - int supplChar = codePointAt(firstUpper); - if (supplChar != Character.toLowerCase(supplChar)) { - break scan; - } - firstUpper += Character.charCount(supplChar); - } else { - if (c != Character.toLowerCase(c)) { - break scan; - } - firstUpper++; - } + // Now check if there are any characters that need to be changed. + for (firstUpper = 0 ; firstUpper < len; ) { + int cp = (int)value[firstUpper]; + int srcCount = 1; + if (cp >= Character.MIN_HIGH_SURROGATE && cp <= Character.MAX_HIGH_SURROGATE) { + cp = codePointAt(firstUpper); + srcCount = Character.charCount(cp); } - return this; + if (cp != Character.toLowerCase(cp)) { // no need to check Character.ERROR + break; + } + firstUpper += srcCount; } + if (firstUpper == len) + return this; char[] result = new char[len]; - int resultOffset = 0; /* result may grow, so i+resultOffset - * is the write location in result */ - - /* Just copy the first few lowerCase characters. */ - System.arraycopy(value, 0, result, 0, firstUpper); - + System.arraycopy(value, 0, result, 0, firstUpper); // Just copy the first few + // lowerCase characters. String lang = locale.getLanguage(); - boolean localeDependent = - (lang == "tr" || lang == "az" || lang == "lt"); - char[] lowerCharArray; - int lowerChar; - int srcChar; + final boolean localeDependent = (lang == "tr" || lang == "az" || lang == "lt"); + if (localeDependent) { + return toLowerCaseEx(result, firstUpper, locale, localeDependent); + } + for (int i = firstUpper; i < len; i++) { + int c = (int)value[i]; + if (c >= Character.MIN_HIGH_SURROGATE && c <= Character.MAX_HIGH_SURROGATE || + c == '\u03A3' || // GREEK CAPITAL LETTER SIGMA + (c = Character.toLowerCase(c)) >= Character.MIN_SUPPLEMENTARY_CODE_POINT) { + return toLowerCaseEx(result, i, locale, localeDependent); + } + result[i] = (char)c; + } + return new String(result, true); + } + + private String toLowerCaseEx(char[] result, int first, Locale locale, boolean localeDependent) { + int resultOffset = first; int srcCount; - for (int i = firstUpper; i < len; i += srcCount) { - srcChar = (int)value[i]; - if ((char)srcChar >= Character.MIN_HIGH_SURROGATE - && (char)srcChar <= Character.MAX_HIGH_SURROGATE) { + for (int i = first; i < value.length; i += srcCount) { + int srcChar = (int)value[i]; + int lowerChar; + char[] lowerCharArray; + srcCount = 1; + if (srcChar >= Character.MIN_HIGH_SURROGATE && + srcChar <= Character.MAX_HIGH_SURROGATE) { srcChar = codePointAt(i); srcCount = Character.charCount(srcChar); - } else { - srcCount = 1; } if (localeDependent || srcChar == '\u03A3') { // GREEK CAPITAL LETTER SIGMA lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale); } else { lowerChar = Character.toLowerCase(srcChar); } - if ((lowerChar == Character.ERROR) - || (lowerChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) { + if (lowerChar != Character.ERROR && + lowerChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + result[resultOffset++] = (char)lowerChar; + } else { if (lowerChar == Character.ERROR) { - lowerCharArray = - ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale); + lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale); } else if (srcCount == 2) { - resultOffset += Character.toChars(lowerChar, result, i + resultOffset) - srcCount; + resultOffset += Character.toChars(lowerChar, result, resultOffset); continue; } else { lowerCharArray = Character.toChars(lowerChar); } - /* Grow result if needed */ int mapLen = lowerCharArray.length; if (mapLen > srcCount) { char[] result2 = new char[result.length + mapLen - srcCount]; - System.arraycopy(result, 0, result2, 0, i + resultOffset); + System.arraycopy(result, 0, result2, 0, resultOffset); result = result2; } for (int x = 0; x < mapLen; ++x) { - result[i + resultOffset + x] = lowerCharArray[x]; + result[resultOffset++] = lowerCharArray[x]; } - resultOffset += (mapLen - srcCount); - } else { - result[i + resultOffset] = (char)lowerChar; } } - return new String(result, 0, len + resultOffset); + return new String(result, 0, resultOffset); } /** @@ -2707,62 +2708,81 @@ if (locale == null) { throw new NullPointerException(); } - int firstLower; final int len = value.length; - /* Now check if there are any characters that need to be changed. */ - scan: { - for (firstLower = 0 ; firstLower < len; ) { - int c = (int)value[firstLower]; - int srcCount; - if ((c >= Character.MIN_HIGH_SURROGATE) - && (c <= Character.MAX_HIGH_SURROGATE)) { - c = codePointAt(firstLower); - srcCount = Character.charCount(c); - } else { - srcCount = 1; - } - int upperCaseChar = Character.toUpperCaseEx(c); - if ((upperCaseChar == Character.ERROR) - || (c != upperCaseChar)) { - break scan; - } - firstLower += srcCount; + // Now check if there are any characters that need to be changed. + for (firstLower = 0 ; firstLower < len; ) { + int cp = (int)value[firstLower]; + int srcCount = 1; + if (cp >= Character.MIN_HIGH_SURROGATE && cp <= Character.MAX_HIGH_SURROGATE) { + cp = codePointAt(firstLower); + srcCount = Character.charCount(cp); } + if (cp != Character.toUpperCaseEx(cp)) { // no need to check Character.ERROR + break; + } + firstLower += srcCount; + } + if (firstLower == len) { return this; } - - /* result may grow, so i+resultOffset is the write location in result */ - int resultOffset = 0; - char[] result = new char[len]; /* may grow */ - - /* Just copy the first few upperCase characters. */ - System.arraycopy(value, 0, result, 0, firstLower); - String lang = locale.getLanguage(); - boolean localeDependent = + final boolean localeDependent = (lang == "tr" || lang == "az" || lang == "lt"); - char[] upperCharArray; - int upperChar; - int srcChar; + + char[] result = new char[len]; + System.arraycopy(value, 0, result, 0, firstLower); // Just copy the first few + // upperCase characters. + if (localeDependent) { + return toUpperCaseEx(result, firstLower, locale, localeDependent); + } + for (int i = firstLower; i < len; i++) { + int cp = (int)value[i]; + if (cp >= Character.MIN_HIGH_SURROGATE && cp <= Character.MAX_HIGH_SURROGATE) { + return toUpperCaseEx(result, i, locale, localeDependent); + } + cp = Character.toUpperCaseEx(cp); + if (cp == Character.ERROR || + cp >= Character.MIN_SUPPLEMENTARY_CODE_POINT) { + return toUpperCaseEx(result, i, locale, localeDependent); + } + /* faster? + int cp = Character.toUpperCaseEx((int)value[i]); + if (cp == Character.ERROR || + cp >= Character.MIN_HIGH_SURROGATE && cp <= Character.MAX_HIGH_SURROGATE || + cp >= Character.MIN_SUPPLEMENTARY_CODE_POINT) { + return toUpperCaseEx(result, i, locale, localeDependent); + } + */ + result[i] = (char)cp; + } + return new String(result, true); + } + + private String toUpperCaseEx(char[] result, int first, Locale locale, + boolean localeDependent) { + int resultOffset = first; int srcCount; - for (int i = firstLower; i < len; i += srcCount) { - srcChar = (int)value[i]; - if ((char)srcChar >= Character.MIN_HIGH_SURROGATE && - (char)srcChar <= Character.MAX_HIGH_SURROGATE) { + for (int i = first; i < value.length; i += srcCount) { + int srcChar = (int)value[i]; + int upperChar; + char[] upperCharArray; + srcCount = 1; + if (srcChar >= Character.MIN_HIGH_SURROGATE && + srcChar <= Character.MAX_HIGH_SURROGATE) { srcChar = codePointAt(i); srcCount = Character.charCount(srcChar); - } else { - srcCount = 1; - } + } if (localeDependent) { upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale); } else { upperChar = Character.toUpperCaseEx(srcChar); } - if ((upperChar == Character.ERROR) - || (upperChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) { + if (upperChar != Character.ERROR && + upperChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + result[resultOffset++] = (char)upperChar; + } else { if (upperChar == Character.ERROR) { if (localeDependent) { upperCharArray = @@ -2771,28 +2791,24 @@ upperCharArray = Character.toUpperCaseCharArray(srcChar); } } else if (srcCount == 2) { - resultOffset += Character.toChars(upperChar, result, i + resultOffset) - srcCount; + resultOffset += Character.toChars(upperChar, result, resultOffset); continue; } else { upperCharArray = Character.toChars(upperChar); } - /* Grow result if needed */ int mapLen = upperCharArray.length; if (mapLen > srcCount) { char[] result2 = new char[result.length + mapLen - srcCount]; - System.arraycopy(result, 0, result2, 0, i + resultOffset); + System.arraycopy(result, 0, result2, 0, resultOffset); result = result2; - } - for (int x = 0; x < mapLen; ++x) { - result[i + resultOffset + x] = upperCharArray[x]; - } - resultOffset += (mapLen - srcCount); - } else { - result[i + resultOffset] = (char)upperChar; + } + for (int x = 0; x < mapLen; ++x) { + result[resultOffset++] = upperCharArray[x]; + } } } - return new String(result, 0, len + resultOffset); + return new String(result, 0, resultOffset); } /** --- old/test/java/lang/String/ToLowerCase.java 2014-01-21 13:47:48.000000000 -0800 +++ new/test/java/lang/String/ToLowerCase.java 2014-01-21 13:47:48.000000000 -0800 @@ -104,6 +104,22 @@ // invalid code point tests: test("\uD800\uD800\uD801A\uDC00\uDC00\uDC00B", Locale.US, "\uD800\uD800\uD801a\uDC00\uDC00\uDC00b"); + // test bmp + supp1 + StringBuilder src = new StringBuilder(0x20000); + StringBuilder exp = new StringBuilder(0x20000); + for (int cp = 0; cp < 0x20000; cp++) { + if (cp >= Character.MIN_HIGH_SURROGATE && cp <= Character.MAX_HIGH_SURROGATE) { + continue; + } + int lowerCase = Character.toLowerCase(cp); + if (lowerCase == -1) { //Character.ERROR + continue; + } + src.appendCodePoint(cp); + exp.appendCodePoint(lowerCase); + } + test(src.toString(), Locale.US, exp.toString()); + } static void test(String in, Locale locale, String expected) {