Return-Path: Delivered-To: apmail-incubator-harmony-commits-archive@www.apache.org Received: (qmail 81660 invoked from network); 15 Mar 2006 12:10:43 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 15 Mar 2006 12:10:43 -0000 Received: (qmail 39619 invoked by uid 500); 15 Mar 2006 12:10:17 -0000 Delivered-To: apmail-incubator-harmony-commits-archive@incubator.apache.org Received: (qmail 39542 invoked by uid 500); 15 Mar 2006 12:10:16 -0000 Mailing-List: contact harmony-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: harmony-dev@incubator.apache.org Delivered-To: mailing list harmony-commits@incubator.apache.org Received: (qmail 39531 invoked by uid 99); 15 Mar 2006 12:10:16 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 15 Mar 2006 04:10:16 -0800 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Wed, 15 Mar 2006 04:10:14 -0800 Received: (qmail 80993 invoked by uid 65534); 15 Mar 2006 12:09:38 -0000 Message-ID: <20060315120933.80902.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r386060 - in /incubator/harmony/enhanced/classlib/trunk/modules/luni/src: main/java/java/lang/Character.java test/java/org/apache/harmony/tests/java/lang/AllTests.java test/java/org/apache/harmony/tests/java/lang/CharacterTest.java Date: Wed, 15 Mar 2006 12:09:05 -0000 To: harmony-commits@incubator.apache.org From: tellison@apache.org X-Mailer: svnmailer-1.0.7 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: tellison Date: Wed Mar 15 04:09:01 2006 New Revision: 386060 URL: http://svn.apache.org/viewcvs?rev=386060&view=rev Log: Apply patch HARMONY-197 ([classlib][luni] Java 5 enhancements for java.lang.Character) Added: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/Character.java incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/AllTests.java Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/Character.java URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/Character.java?rev=386060&r1=386059&r2=386060&view=diff ============================================================================== --- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/Character.java (original) +++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/Character.java Wed Mar 15 04:09:01 2006 @@ -15,22 +15,48 @@ package java.lang; - import java.io.Serializable; import org.apache.harmony.luni.util.BinarySearch; - /** - * Characters are objects (i.e. non-base types) which represent char values. It - * also provides a number of methods for the lexicographic categorization of - * char values. + *

+ * Character is the wrapper for the primitive type char. This + * class also provides a number of utility methods for working with + * chars. + *

+ * + *

+ * Character data is based upon the Unicode Standard, 4.0. The Unicode + * specification, character tables and other information is available at http://www.unicode.org/. + *

+ * + *

+ * Unicode characters are referred to as code points. The range of valid + * code points is U+0000 to U+10FFFF. The Basic Multilingual Plane (BMP) + * is the code point range U+0000 to U+FFFF. Characters above the BMP are + * referred to as Supplementary Characters. On the Java platform, UTF-16 + * encoding and char pairs are used to represent code points in + * the supplementary range. A pair of char values that represent + * a supplementary character are made up of a high surrogate with a + * value range of 0xD800 to 0xDBFF and a low surrogate with a value + * range of 0xDC00 to 0xDFFF. + *

+ * + *

+ * On the Java platform a char value represents either a single + * BMP code point or a UTF-16 unit that's part of a surrogate pair. The + * int type is used to represent all Unicode code points. + *

+ * + * @since 1.0 */ public final class Character implements Serializable, Comparable { - + //TODO Add Comparable when support for generics is available. private static final long serialVersionUID = 3786198910865385080L; - final char value; + private final char value; /** * The minimum possible Character value. @@ -205,113 +231,229 @@ /** * Unicode category constant Pi. + * @since 1.4 */ public static final byte INITIAL_QUOTE_PUNCTUATION = 29; /** * Unicode category constant Pf. + * @since 1.4 */ public static final byte FINAL_QUOTE_PUNCTUATION = 30; /** * Unicode bidirectional constant. + * @since 1.4 */ public static final byte DIRECTIONALITY_UNDEFINED = -1; /** * Unicode bidirectional constant L. + * @since 1.4 */ public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = 0; /** * Unicode bidirectional constant R. + * @since 1.4 */ public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = 1; /** * Unicode bidirectional constant AL. + * @since 1.4 */ public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = 2; /** * Unicode bidirectional constant EN. + * @since 1.4 */ public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = 3; /** * Unicode bidirectional constant ES. + * @since 1.4 */ public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = 4; /** * Unicode bidirectional constant ET. + * @since 1.4 */ public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = 5; /** * Unicode bidirectional constant AN. + * @since 1.4 */ public static final byte DIRECTIONALITY_ARABIC_NUMBER = 6; /** * Unicode bidirectional constant CS. + * @since 1.4 */ public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = 7; /** * Unicode bidirectional constant NSM. + * @since 1.4 */ public static final byte DIRECTIONALITY_NONSPACING_MARK = 8; /** * Unicode bidirectional constant BN. + * @since 1.4 */ public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = 9; /** * Unicode bidirectional constant B. + * @since 1.4 */ public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = 10; /** * Unicode bidirectional constant S. + * @since 1.4 */ public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = 11; /** * Unicode bidirectional constant WS. + * @since 1.4 */ public static final byte DIRECTIONALITY_WHITESPACE = 12; /** * Unicode bidirectional constant ON. + * @since 1.4 */ public static final byte DIRECTIONALITY_OTHER_NEUTRALS = 13; /** * Unicode bidirectional constant LRE. + * @since 1.4 */ public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = 14; /** * Unicode bidirectional constant LRO. + * @since 1.4 */ public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = 15; /** * Unicode bidirectional constant RLE. + * @since 1.4 */ public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = 16; /** * Unicode bidirectional constant RLO. + * @since 1.4 */ public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = 17; /** * Unicode bidirectional constant PDF. + * @since 1.4 */ public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18; + + /** + *

+ * Minimum value of a high surrogate or leading surrogate unit in UTF-16 + * encoding - '\uD800'. + *

+ * + * @since 1.5 + */ + public static final char MIN_HIGH_SURROGATE = '\uD800'; + + /** + *

+ * Maximum value of a high surrogate or leading surrogate unit in UTF-16 + * encoding - '\uDBFF'. + *

+ * + * @since 1.5 + */ + public static final char MAX_HIGH_SURROGATE = '\uDBFF'; + + /** + *

+ * Minimum value of a low surrogate or trailing surrogate unit in UTF-16 + * encoding - '\uDC00'. + *

+ * + * @since 1.5 + */ + public static final char MIN_LOW_SURROGATE = '\uDC00'; + + /** + * Maximum value of a low surrogate or trailing surrogate unit in UTF-16 + * encoding - '\uDFFF'. + *

+ * + * @since 1.5 + */ + public static final char MAX_LOW_SURROGATE = '\uDFFF'; + + /** + *

+ * Minimum value of a surrogate unit in UTF-16 encoding - '\uD800'. + *

+ * + * @since 1.5 + */ + public static final char MIN_SURROGATE = '\uD800'; + + /** + *

+ * Maximum value of a surrogate unit in UTF-16 encoding - '\uDFFF'. + *

+ * + * @since 1.5 + */ + public static final char MAX_SURROGATE = '\uDFFF'; + + /** + *

+ * Minimum value of a supplementary code point - U+0010000. + *

+ * + * @since 1.5 + */ + public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x10000; + + /** + *

+ * Minimum code point value - U+0000. + *

+ * + * @since 1.5 + */ + public static final int MIN_CODE_POINT = 0x000000; + + /** + *

+ * Maximum code point value - U+10FFFF. + *

+ * + * @since 1.5 + */ + public static final int MAX_CODE_POINT = 0x10FFFF; + + /** + *

+ * Constant for the number of bits to represent a char in + * two's compliment form. + *

+ * + * @since 1.5 + */ + public static final int SIZE = 16; // Unicode 3.0.1 (same as Unicode 3.0.0) private static final String bidiKeys = "\u0000\t\f\u000e\u001c\u001f!#&+/1:c - * in the supplied radix. The value of radix is must be - * between MIN_RADIX and MAX_RADIX inclusive. - * - * @param c - * the character - * @param radix - * the radix - * @return if radix lies between {@link #MIN_RADIX} and - * {@link #MAX_RADIX} then the value of the character in the radix, - * otherwise -1. - */ + + /* + * Provides a cache for the 'valueOf' method. A size of 512 should cache the + * first couple pages of Unicode, which includes the ASCII/Latin-1 + * characters, which other parts of this class are optimized for. + */ + private static final Character[] CACHE = new Character[512]; + + /** + *

+ * Returns a Character instance for the char + * value passed. This method is preferred over the constructor, as this + * method may maintain a cache of instances. + *

+ * + * @param c The char value. + * @return A Character instance. + * @since 1.5 + */ + public static Character valueOf(char c) { + if (c > CACHE.length) + return new Character(c); + synchronized (CACHE) { + Character ch = CACHE[c]; + if (ch == null) + CACHE[c] = ch = new Character(c); + return ch; + } + } + + /** + *

+ * A test for determining if the codePoint is a valid Unicode + * code point. + *

+ * + * @param codePoint The code point to test. + * @return A boolean value. + * @since 1.5 + */ + public static boolean isValidCodePoint(int codePoint) { + return (MIN_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint); + } + + /** + *

+ * A test for determining if the codePoint is within the + * supplementary code point range. + *

+ * + * @param codePoint The code point to test. + * @return A boolean value. + * @since 1.5 + */ + public static boolean isSupplementaryCodePoint(int codePoint) { + return (MIN_SUPPLEMENTARY_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint); + } + + /** + *

+ * A test for determining if the char is a high + * surrogate/leading surrogate unit that's used for representing + * supplementary characters in UTF-16 encoding. + *

+ * + * @param ch The char unit to test. + * @return A boolean value. + * @since 1.5 + * @see #isLowSurrogate(char) + */ + public static boolean isHighSurrogate(char ch) { + return (MIN_HIGH_SURROGATE <= ch && MAX_HIGH_SURROGATE >= ch); + } + + /** + *

+ * A test for determining if the char is a high + * surrogate/leading surrogate unit that's used for representing + * supplementary characters in UTF-16 encoding. + *

+ * + * @param ch The char unit to test. + * @return A boolean value. + * @since 1.5 + * @see #isHighSurrogate(char) + */ + public static boolean isLowSurrogate(char ch) { + return (MIN_LOW_SURROGATE <= ch && MAX_LOW_SURROGATE >= ch); + } + + /** + *

+ * A test for determining if the char pair is a valid + * surrogate pair. + *

+ * + * @param high The high surrogate unit to test. + * @param low The low surrogate unit to test. + * @return A boolean value. + * @since 1.5 + * @see #isHighSurrogate(char) + * @see #isLowSurrogate(char) + */ + public static boolean isSurrogatePair(char high, char low) { + return (isHighSurrogate(high) && isLowSurrogate(low)); + } + + /** + *

+ * Calculates the number of char values required to represent + * the Unicode code point. This method only tests if the + * codePoint is greater than or equal to 0x10000, + * in which case 2 is returned, otherwise 1. + * To test if the code point is valid, use the + * {@link #isValidCodePoint(int)} method. + *

+ * + * @param codePoint The code point to test. + * @return An int value of 2 or 1. + * @since 1.5 + * @see #isValidCodePoint(int) + * @see #isSupplementaryCodePoint(int) + */ + public static int charCount(int codePoint) { + return (codePoint >= 0x10000 ? 2 : 1); + } + + /** + *

+ * Converts a surrogate pair into a Unicode code point. This method assume + * that the pair are valid surrogates. If the pair are NOT valid surrogates, + * then the result is indeterminate. The + * {@link #isSurrogatePair(char, char)} method should be used prior to this + * method to validate the pair. + *

+ * + * @param high The high surrogate unit. + * @param low The low surrogate unit. + * @return The decoded code point. + * @since 1.5 + * @see #isSurrogatePair(char, char) + */ + public static int toCodePoint(char high, char low) { + // See RFC 2781, Section 2.2 + // http://www.faqs.org/rfcs/rfc2781.html + int h = (high & 0x3FF) << 10; + int l = low & 0x3FF; + return (h | l) + 0x10000; + } + + /** + *

+ * Returns the code point at the index in the CharSequence. + * If char unit at the index is a high-surrogate unit, the + * next index is less than the length of the sequence and the + * char unit at the next index is a low surrogate unit, then + * the code point represented by the pair is returned; otherwise the + * char unit at the index is returned. + *

+ * + * @param seq The sequence of char units. + * @param index The index into the seq to retrieve and + * convert. + * @return The Unicode code point. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if the index is negative + * or greater than or equal to seq.length(). + * @since 1.5 + */ + public static int codePointAt(CharSequence seq, int index) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length(); + if (index < 0 || index >= len) + throw new IndexOutOfBoundsException(); + + char high = seq.charAt(index++); + if (index >= len) + return high; + char low = seq.charAt(index); + if (isSurrogatePair(high, low)) + return toCodePoint(high, low); + return high; + } + + /** + *

+ * Returns the code point at the index in the char[]. If + * char unit at the index is a high-surrogate unit, the next + * index is less than the length of the sequence and the char + * unit at the next index is a low surrogate unit, then the code point + * represented by the pair is returned; otherwise the char + * unit at the index is returned. + *

+ * + * @param seq The sequence of char units. + * @param index The index into the seq to retrieve and + * convert. + * @return The Unicode code point. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if the index is negative + * or greater than or equal to seq.length(). + * @since 1.5 + */ + public static int codePointAt(char[] seq, int index) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length; + if (index < 0 || index >= len) + throw new IndexOutOfBoundsException(); + + char high = seq[index++]; + if (index >= len) + return high; + char low = seq[index]; + if (isSurrogatePair(high, low)) + return toCodePoint(high, low); + return high; + } + + /** + *

+ * Returns the code point at the index in the char[] that's + * within the limit. If char unit at the index is a + * high-surrogate unit, the next index is less than the limit + * and the char unit at the next index is a low surrogate + * unit, then the code point represented by the pair is returned; otherwise + * the char unit at the index is returned. + *

+ * + * @param seq The sequence of char units. + * @param index The index into the seq to retrieve and + * convert. + * @param limit The exclusive index into the seq that marks + * the end of the units that can be used. + * @return The Unicode code point. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if the index is + * negative, greater than or equal to limit, + * limit is negative or limit is + * greater than the length of seq. + * @since 1.5 + */ + public static int codePointAt(char[] seq, int index, int limit) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length; + if (index < 0 || index >= limit || limit < 0 || limit > len) + throw new IndexOutOfBoundsException(); + + char high = seq[index++]; + if (index >= limit) + return high; + char low = seq[index]; + if (isSurrogatePair(high, low)) + return toCodePoint(high, low); + return high; + } + + /** + *

+ * Returns the Unicode code point that proceeds the index in + * the CharSequence. If the char unit at + * index - 1 is within the low surrogate range, the value + * index - 2 isn't negative and the char unit + * at index - 2 is within the high surrogate range, then the + * supplementary code point made up of the surrogate pair is returned; + * otherwise, the char value at index - 1 is + * returned. + *

+ * + * @param seq The CharSequence to search. + * @param index The index into the seq. + * @return A Unicode code point. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if index is less than 1 + * or greater than seq.length(). + * @since 1.5 + */ + public static int codePointBefore(CharSequence seq, int index) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length(); + if (index < 1 || index > len) + throw new IndexOutOfBoundsException(); + + char low = seq.charAt(--index); + if (--index < 0) + return low; + char high = seq.charAt(index); + if (isSurrogatePair(high, low)) + return toCodePoint(high, low); + return low; + } + + /** + *

+ * Returns the Unicode code point that proceeds the index in + * the char[]. If the char unit at + * index - 1 is within the low surrogate range, the value + * index - 2 isn't negative and the char unit + * at index - 2 is within the high surrogate range, then the + * supplementary code point made up of the surrogate pair is returned; + * otherwise, the char value at index - 1 is + * returned. + *

+ * + * @param seq The char[] to search. + * @param index The index into the seq. + * @return A Unicode code point. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if index is less than 1 + * or greater than seq.length. + * @since 1.5 + */ + public static int codePointBefore(char[] seq, int index) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length; + if (index < 1 || index > len) + throw new IndexOutOfBoundsException(); + + char low = seq[--index]; + if (--index < 0) + return low; + char high = seq[index]; + if (isSurrogatePair(high, low)) + return toCodePoint(high, low); + return low; + } + + /** + *

+ * Returns the Unicode code point that proceeds the index in + * the char[] and isn't less than start. If + * the char unit at index - 1 is within the + * low surrogate range, the value index - 2 isn't less than + * start and the char unit at + * index - 2 is within the high surrogate range, then the + * supplementary code point made up of the surrogate pair is returned; + * otherwise, the char value at index - 1 is + * returned. + *

+ * + * @param seq The char[] to search. + * @param index The index into the seq. + * @return A Unicode code point. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if index is less than or + * equal to start, index is greater + * than seq.length, start is not + * negative and start is greater than + * seq.length. + * @since 1.5 + */ + public static int codePointBefore(char[] seq, int index, int start) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length; + if (index <= start || index > len || start < 0 || start >= len) + throw new IndexOutOfBoundsException(); + + char low = seq[--index]; + if (--index < start) + return low; + char high = seq[index]; + if (isSurrogatePair(high, low)) + return toCodePoint(high, low); + return low; + } + + /** + *

+ * Converts the Unicode code point, codePoint, into a UTF-16 + * encoded sequence and copies the value(s) into the + * char[] dst, starting at the index + * dstIndex. + *

+ * + * @param codePoint The Unicode code point to encode. + * @param dst The char[] to copy the encoded value into. + * @param dstIndex The index to start copying into dst. + * @return The number of char value units copied into + * dst. + * @throws IllegalArgumentException if codePoint is not a + * valid Unicode code point. + * @throws NullPointerException if dst is null. + * @throws IndexOutOfBoundsException if dstIndex is negative, + * greater than or equal to dst.length or equals + * dst.length - 1 when codePoint is a + * {@link #isSupplementaryCodePoint(int) supplementary code point}. + * @since 1.5 + */ + public static int toChars(int codePoint, char[] dst, int dstIndex) { + if (!isValidCodePoint(codePoint)) + throw new IllegalArgumentException(); + if (dst == null) + throw new NullPointerException(); + if (dstIndex < 0 || dstIndex >= dst.length) + throw new IndexOutOfBoundsException(); + + if (isSupplementaryCodePoint(codePoint)) { + if (dstIndex == dst.length - 1) + throw new IndexOutOfBoundsException(); + // See RFC 2781, Section 2.1 + // http://www.faqs.org/rfcs/rfc2781.html + int cpPrime = codePoint - 0x10000; + int high = 0xD800 | ((cpPrime >> 10) & 0x3FF); + int low = 0xDC00 | (cpPrime & 0x3FF); + dst[dstIndex] = (char) high; + dst[dstIndex + 1] = (char) low; + return 2; + } + + dst[dstIndex] = (char) codePoint; + return 1; + } + + /** + *

+ * Converts the Unicode code point, codePoint, into a UTF-16 + * encoded sequence that is returned as a char[]. + *

+ * + * @param codePoint The Unicode code point to encode. + * @return The UTF-16 encoded char sequence; if code point is + * a {@link #isSupplementaryCodePoint(int) supplementary code point}, + * then a 2 char array is returned, otherwise a 1 + * char array is returned. + * @throws IllegalArgumentException if codePoint is not a + * valid Unicode code point. + * @since 1.5 + */ + public static char[] toChars(int codePoint) { + if (!isValidCodePoint(codePoint)) + throw new IllegalArgumentException(); + + if (isSupplementaryCodePoint(codePoint)) { + int cpPrime = codePoint - 0x10000; + int high = 0xD800 | ((cpPrime >> 10) & 0x3FF); + int low = 0xDC00 | (cpPrime & 0x3FF); + return new char[] { (char) high, (char) low }; + } + return new char[] { (char) codePoint }; + } + + /** + *

+ * Counts the number of Unicode code points in the subsequence of the + * CharSequence, as delineated by the + * beginIndex and endIndex. Any surrogate + * values with missing pair values will be counted as 1 code point. + *

+ * + * @param seq The CharSequence to look through. + * @param beginIndex The inclusive index to begin counting at. + * @param endIndex The exclusive index to stop counting at. + * @return The number of Unicode code points. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if beginIndex is + * negative, greater than seq.length() or greater + * than endIndex. + * @since 1.5 + */ + public static int codePointCount(CharSequence seq, int beginIndex, + int endIndex) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length(); + if (beginIndex < 0 || endIndex > len || beginIndex > endIndex) + throw new IndexOutOfBoundsException(); + + int result = 0; + for (int i = beginIndex; i < endIndex; i++) { + char c = seq.charAt(i); + if (isHighSurrogate(c)) { + if (++i < endIndex) { + c = seq.charAt(i); + if (!isLowSurrogate(c)) + result++; + } + } + result++; + } + return result; + } + + /** + *

+ * Counts the number of Unicode code points in the subsequence of the + * char[], as delineated by the offset and + * count. Any surrogate values with missing pair values will + * be counted as 1 code point. + *

+ * + * @param seq The char[] to look through. + * @param offset The inclusive index to begin counting at. + * @param count The number of char values to look through in + * seq. + * @return The number of Unicode code points. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if offset or + * count is negative or if endIndex is + * greater than seq.length. + * @since 1.5 + */ + public static int codePointCount(char[] seq, int offset, int count) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length; + int endIndex = offset + count; + if (offset < 0 || count < 0 || endIndex > len) + throw new IndexOutOfBoundsException(); + + int result = 0; + for (int i = offset; i < endIndex; i++) { + char c = seq[i]; + if (isHighSurrogate(c)) { + if (++i < endIndex) { + c = seq[i]; + if (!isLowSurrogate(c)) + result++; + } + } + result++; + } + return result; + } + + /** + *

+ * Determines the index into the CharSequence that is offset + * (measured in code points and specified by codePointOffset), + * from the index argument. + *

+ * + * @param seq The CharSequence to find the index within. + * @param index The index to begin from, within the + * CharSequence. + * @param codePointOffset The number of code points to look back or + * forwards; may be a negative or positive value. + * @return The calculated index that is codePointOffset code + * points from index. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if index is negative, + * greater than seq.length(), there aren't enough + * values in seq after index or before + * index if codePointOffset is + * negative. + * @since 1.5 + */ + public static int offsetByCodePoints(CharSequence seq, int index, + int codePointOffset) { + if (seq == null) + throw new NullPointerException(); + int len = seq.length(); + if (index < 0 || index > len) + throw new IndexOutOfBoundsException(); + + if (codePointOffset == 0) + return index; + + if (codePointOffset > 0) { + int codePoints = codePointOffset; + int i = index; + while (codePoints > 0) { + codePoints--; + if (i >= len) + throw new IndexOutOfBoundsException(); + if (isHighSurrogate(seq.charAt(i))) { + int next = i + 1; + if (next < len && isLowSurrogate(seq.charAt(next))) + i++; + } + i++; + } + return i; + } + + assert codePointOffset < 0; + int codePoints = -codePointOffset; + int i = index; + while (codePoints > 0) { + codePoints--; + i--; + if (i < 0) + throw new IndexOutOfBoundsException(); + if (isLowSurrogate(seq.charAt(i))) { + int prev = i - 1; + if (prev >= 0 && isHighSurrogate(seq.charAt(prev))) + i--; + } + } + return i; + } + + /** + *

+ * Determines the index into the char[] that is offset + * (measured in code points and specified by codePointOffset), + * from the index argument and is within the subsequence as + * delineated by start and count. + *

+ * + * @param seq The char[] to find the index within. + * + * @param index The index to begin from, within the char[]. + * @param codePointOffset The number of code points to look back or + * forwards; may be a negative or positive value. + * @param start The inclusive index that marks the beginning of the + * subsequence. + * @param count The number of char values to include within + * the subsequence. + * @return The calculated index that is codePointOffset code + * points from index. + * @throws NullPointerException if seq is null. + * @throws IndexOutOfBoundsException if start or + * count is negative, start + count + * greater than seq.length, index is + * less than start, index is greater + * than start + count or there aren't enough values + * in seq after index or before + * index if codePointOffset is + * negative. + * @since 1.5 + */ + public static int offsetByCodePoints(char[] seq, int start, int count, + int index, int codePointOffset) { + if (seq == null) + throw new NullPointerException(); + int end = start + count; + if (start < 0 || count < 0 || end > seq.length || index < start + || index > end) + throw new IndexOutOfBoundsException(); + + if (codePointOffset == 0) + return index; + + if (codePointOffset > 0) { + int codePoints = codePointOffset; + int i = index; + while (codePoints > 0) { + codePoints--; + if (i >= end) + throw new IndexOutOfBoundsException(); + if (isHighSurrogate(seq[i])) { + int next = i + 1; + if (next < end && isLowSurrogate(seq[next])) + i++; + } + i++; + } + return i; + } + + assert codePointOffset < 0; + int codePoints = -codePointOffset; + int i = index; + while (codePoints > 0) { + codePoints--; + i--; + if (i < start) + throw new IndexOutOfBoundsException(); + if (isLowSurrogate(seq[i])) { + int prev = i - 1; + if (prev >= start && isHighSurrogate(seq[prev])) + i--; + } + } + return i; + } + + /** + * Convenience method to determine the value of character c + * in the supplied radix. The value of radix is must be + * between MIN_RADIX and MAX_RADIX inclusive. + * + * @param c the character + * @param radix the radix + * @return if radix lies between {@link #MIN_RADIX} and + * {@link #MAX_RADIX} then the value of the character in the radix, + * otherwise -1. + */ public static int digit(char c, int radix) { if (radix >= MIN_RADIX && radix <= MAX_RADIX) { if (c < 128) { Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/AllTests.java URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/AllTests.java?rev=386060&r1=386059&r2=386060&view=diff ============================================================================== --- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/AllTests.java (original) +++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/AllTests.java Wed Mar 15 04:09:01 2006 @@ -28,13 +28,14 @@ TestSuite suite = new TestSuite( "Test for org.apache.harmony.tests.java.lang"); //$JUnit-BEGIN$ + suite.addTestSuite(CharacterTest.class); + suite.addTestSuite(ByteTest.class); suite.addTestSuite(FloatTest.class); + suite.addTestSuite(StringBuilderTest.class); + suite.addTestSuite(BooleanTest.class); suite.addTestSuite(StringBufferTest.class); suite.addTestSuite(SecurityManagerTest.class); suite.addTestSuite(DoubleTest.class); - suite.addTestSuite(StringBuilderTest.class); - suite.addTestSuite(BooleanTest.class); - suite.addTestSuite(ByteTest.class); //$JUnit-END$ return suite; } Added: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java?rev=386060&view=auto ============================================================================== --- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java (added) +++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java Wed Mar 15 04:09:01 2006 @@ -0,0 +1,576 @@ +/* Copyright 2006 The Apache Software Foundation or its licensors, as applicable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.tests.java.lang; + +import java.util.Arrays; + +import junit.framework.TestCase; + +public class CharacterTest extends TestCase { + + public void test_valueOfC() { + // test the cache range + for (char c = '\u0000'; c < 512; c++) { + Character e = new Character(c); + Character a = Character.valueOf(c); + assertEquals(e, a); + + // WARN: this assertion may not be valid on other JREs + assertSame(Character.valueOf(c), Character.valueOf(c)); + } + // test the rest of the chars + for (int c = '\u0512'; c <= Character.MAX_VALUE; c++) { + assertEquals(new Character((char) c), Character.valueOf((char) c)); + } + } + + public void test_isValidCodePointI() { + assertFalse(Character.isValidCodePoint(-1)); + assertTrue(Character.isValidCodePoint(0)); + assertTrue(Character.isValidCodePoint(1)); + assertFalse(Character.isValidCodePoint(Integer.MAX_VALUE)); + + for (int c = '\u0000'; c <= 0x10FFFF; c++) { + assertTrue(Character.isValidCodePoint(c)); + } + + assertFalse(Character.isValidCodePoint(0x10FFFF + 1)); + } + + public void test_isSupplementaryCodePointI() { + assertFalse(Character.isSupplementaryCodePoint(-1)); + + for (int c = '\u0000'; c <= '\uFFFF'; c++) { + assertFalse(Character.isSupplementaryCodePoint(c)); + } + + for (int c = 0xFFFF + 1; c <= 0x10FFFF; c++) { + assertTrue(Character.isSupplementaryCodePoint(c)); + } + + assertFalse(Character.isSupplementaryCodePoint(0x10FFFF + 1)); + } + + public void test_isHighSurrogateC() { + // (\uD800-\uDBFF) + assertFalse(Character.isHighSurrogate((char) ('\uD800' - 1))); + for (int c = '\uD800'; c <= '\uDBFF'; c++) { + assertTrue(Character.isHighSurrogate((char) c)); + } + assertFalse(Character.isHighSurrogate((char) ('\uDBFF' + 1))); + assertFalse(Character.isHighSurrogate('\uFFFF')); + } + + public void test_isLowSurrogateC() { + // (\uDC00-\uDFFF) + assertFalse(Character.isLowSurrogate((char) ('\uDC00' - 1))); + for (int c = '\uDC00'; c <= '\uDFFF'; c++) { + assertTrue(Character.isLowSurrogate((char) c)); + } + assertFalse(Character.isLowSurrogate((char) ('\uDFFF' + 1))); + } + + public void test_isSurrogatePairCC() { + assertFalse(Character.isSurrogatePair('\u0000', '\u0000')); + assertFalse(Character.isSurrogatePair('\u0000', '\uDC00')); + + assertTrue(Character.isSurrogatePair('\uD800', '\uDC00')); + assertTrue(Character.isSurrogatePair('\uD800', '\uDFFF')); + assertTrue(Character.isSurrogatePair('\uDBFF', '\uDFFF')); + + assertFalse(Character.isSurrogatePair('\uDBFF', '\uF000')); + } + + public void test_charCountI() { + + for (int c = '\u0000'; c <= '\uFFFF'; c++) { + assertEquals(1, Character.charCount(c)); + } + + for (int c = 0xFFFF + 1; c <= 0x10FFFF; c++) { + assertEquals(2, Character.charCount(c)); + } + + // invalid code points work in this method + assertEquals(2, Character.charCount(Integer.MAX_VALUE)); + } + + public void test_toCodePointCC() { + int result = Character.toCodePoint('\uD800', '\uDC00'); + assertEquals(0x00010000, result); + + result = Character.toCodePoint('\uD800', '\uDC01'); + assertEquals(0x00010001, result); + + result = Character.toCodePoint('\uD801', '\uDC01'); + assertEquals(0x00010401, result); + + result = Character.toCodePoint('\uDBFF', '\uDFFF'); + assertEquals(0x00010FFFF, result); + } + + public void test_codePointAtLjava_lang_CharSequenceI() { + + assertEquals('a', Character.codePointAt((CharSequence) "abc", 0)); + assertEquals('b', Character.codePointAt((CharSequence) "abc", 1)); + assertEquals('c', Character.codePointAt((CharSequence) "abc", 2)); + assertEquals(0x10000, Character.codePointAt( + (CharSequence) "\uD800\uDC00", 0)); + assertEquals('\uDC00', Character.codePointAt( + (CharSequence) "\uD800\uDC00", 1)); + + try { + Character.codePointAt((CharSequence) null, 0); + fail("No NPE."); + } catch (NullPointerException e) { + } + + try { + Character.codePointAt((CharSequence) "abc", -1); + fail("No IOOBE, negative index."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointAt((CharSequence) "abc", 4); + fail("No IOOBE, index too large."); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_codePointAt$CI() { + + assertEquals('a', Character.codePointAt("abc".toCharArray(), 0)); + assertEquals('b', Character.codePointAt("abc".toCharArray(), 1)); + assertEquals('c', Character.codePointAt("abc".toCharArray(), 2)); + assertEquals(0x10000, Character.codePointAt("\uD800\uDC00" + .toCharArray(), 0)); + assertEquals('\uDC00', Character.codePointAt("\uD800\uDC00" + .toCharArray(), 1)); + + try { + Character.codePointAt((char[]) null, 0); + fail("No NPE."); + } catch (NullPointerException e) { + } + + try { + Character.codePointAt("abc".toCharArray(), -1); + fail("No IOOBE, negative index."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointAt("abc".toCharArray(), 4); + fail("No IOOBE, index too large."); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_codePointAt$CII() { + + assertEquals('a', Character.codePointAt("abc".toCharArray(), 0, 3)); + assertEquals('b', Character.codePointAt("abc".toCharArray(), 1, 3)); + assertEquals('c', Character.codePointAt("abc".toCharArray(), 2, 3)); + assertEquals(0x10000, Character.codePointAt("\uD800\uDC00" + .toCharArray(), 0, 2)); + assertEquals('\uDC00', Character.codePointAt("\uD800\uDC00" + .toCharArray(), 1, 2)); + assertEquals('\uD800', Character.codePointAt("\uD800\uDC00" + .toCharArray(), 0, 1)); + + try { + Character.codePointAt((char[]) null, 0, 1); + fail("No NPE."); + } catch (NullPointerException e) { + } + + try { + Character.codePointAt("abc".toCharArray(), -1, 3); + fail("No IOOBE, negative index."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointAt("abc".toCharArray(), 4, 3); + fail("No IOOBE, index too large."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointAt("abc".toCharArray(), 2, 1); + fail("No IOOBE, index larger than limit."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointAt("abc".toCharArray(), 2, -1); + fail("No IOOBE, limit is negative."); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_codePointBeforeLjava_lang_CharSequenceI() { + + assertEquals('a', Character.codePointBefore((CharSequence) "abc", 1)); + assertEquals('b', Character.codePointBefore((CharSequence) "abc", 2)); + assertEquals('c', Character.codePointBefore((CharSequence) "abc", 3)); + assertEquals(0x10000, Character.codePointBefore( + (CharSequence) "\uD800\uDC00", 2)); + assertEquals('\uD800', Character.codePointBefore( + (CharSequence) "\uD800\uDC00", 1)); + + try { + Character.codePointBefore((CharSequence) null, 0); + fail("No NPE."); + } catch (NullPointerException e) { + } + + try { + Character.codePointBefore((CharSequence) "abc", 0); + fail("No IOOBE, index below one."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointBefore((CharSequence) "abc", 4); + fail("No IOOBE, index too large."); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_codePointBefore$CI() { + + assertEquals('a', Character.codePointBefore("abc".toCharArray(), 1)); + assertEquals('b', Character.codePointBefore("abc".toCharArray(), 2)); + assertEquals('c', Character.codePointBefore("abc".toCharArray(), 3)); + assertEquals(0x10000, Character.codePointBefore("\uD800\uDC00" + .toCharArray(), 2)); + assertEquals('\uD800', Character.codePointBefore("\uD800\uDC00" + .toCharArray(), 1)); + + try { + Character.codePointBefore((char[]) null, 0); + fail("No NPE."); + } catch (NullPointerException e) { + } + + try { + Character.codePointBefore("abc".toCharArray(), -1); + fail("No IOOBE, negative index."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointBefore("abc".toCharArray(), 4); + fail("No IOOBE, index too large."); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_codePointBefore$CII() { + + assertEquals('a', Character.codePointBefore("abc".toCharArray(), 1, 0)); + assertEquals('b', Character.codePointBefore("abc".toCharArray(), 2, 0)); + assertEquals('c', Character.codePointBefore("abc".toCharArray(), 3, 0)); + assertEquals(0x10000, Character.codePointBefore("\uD800\uDC00" + .toCharArray(), 2, 0)); + assertEquals('\uDC00', Character.codePointBefore("\uD800\uDC00" + .toCharArray(), 2, 1)); + assertEquals('\uD800', Character.codePointBefore("\uD800\uDC00" + .toCharArray(), 1, 0)); + + try { + Character.codePointBefore((char[]) null, 1, 0); + fail("No NPE."); + } catch (NullPointerException e) { + } + + try { + Character.codePointBefore("abc".toCharArray(), 0, 1); + fail("No IOOBE, index less than start."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointBefore("abc".toCharArray(), 4, 0); + fail("No IOOBE, index larger than length."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointBefore("abc".toCharArray(), 2, -1); + fail("No IOOBE, start is negative."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointBefore("abc".toCharArray(), 2, 4); + fail("No IOOBE, start larger than length."); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_toCharsI$CI() { + char[] dst = new char[2]; + int result = Character.toChars(0x10000, dst, 0); + assertEquals(2, result); + assertTrue(Arrays.equals(new char[] { '\uD800', '\uDC00' }, dst)); + + result = Character.toChars(0x10001, dst, 0); + assertEquals(2, result); + assertTrue(Arrays.equals(new char[] { '\uD800', '\uDC01' }, dst)); + + result = Character.toChars(0x10401, dst, 0); + assertEquals(2, result); + assertTrue(Arrays.equals(new char[] { '\uD801', '\uDC01' }, dst)); + + result = Character.toChars(0x10FFFF, dst, 0); + assertEquals(2, result); + assertTrue(Arrays.equals(new char[] { '\uDBFF', '\uDFFF' }, dst)); + + try { + Character.toChars(Integer.MAX_VALUE, new char[2], 0); + fail("No IAE, invalid code point."); + } catch (IllegalArgumentException e) { + } + + try { + Character.toChars('a', null, 0); + fail("No NPE, null char[]."); + } catch (NullPointerException e) { + } + + try { + Character.toChars('a', new char[1], -1); + fail("No IOOBE, negative index."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.toChars('a', new char[1], 1); + fail("No IOOBE, index equal to length."); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_toCharsI() { + assertTrue(Arrays.equals(new char[] { '\uD800', '\uDC00' }, Character + .toChars(0x10000))); + assertTrue(Arrays.equals(new char[] { '\uD800', '\uDC01' }, Character + .toChars(0x10001))); + assertTrue(Arrays.equals(new char[] { '\uD801', '\uDC01' }, Character + .toChars(0x10401))); + assertTrue(Arrays.equals(new char[] { '\uDBFF', '\uDFFF' }, Character + .toChars(0x10FFFF))); + + try { + Character.toChars(Integer.MAX_VALUE); + fail("No IAE, invalid code point."); + } catch (IllegalArgumentException e) { + } + } + + public void test_codePointCountLjava_lang_CharSequenceII() { + assertEquals(1, Character.codePointCount("\uD800\uDC00", 0, 2)); + assertEquals(1, Character.codePointCount("\uD800\uDC01", 0, 2)); + assertEquals(1, Character.codePointCount("\uD801\uDC01", 0, 2)); + assertEquals(1, Character.codePointCount("\uDBFF\uDFFF", 0, 2)); + + assertEquals(3, Character.codePointCount("a\uD800\uDC00b", 0, 4)); + assertEquals(4, Character.codePointCount("a\uD800\uDC00b\uD800", 0, 5)); + + try { + Character.codePointCount((CharSequence) null, 0, 1); + fail("No NPE, null char sequence."); + } catch (NullPointerException e) { + } + + try { + Character.codePointCount("abc", -1, 1); + fail("No IOOBE, negative start."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointCount("abc", 0, 4); + fail("No IOOBE, end greater than length."); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.codePointCount("abc", 2, 1); + fail("No IOOBE, end greater than start."); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_offsetByCodePointsLjava_lang_CharSequenceII() { + int result = Character.offsetByCodePoints("a\uD800\uDC00b", 0, 2); + assertEquals(3, result); + + result = Character.offsetByCodePoints("abcd", 3, -1); + assertEquals(2, result); + + result = Character.offsetByCodePoints("a\uD800\uDC00b", 0, 3); + assertEquals(4, result); + + result = Character.offsetByCodePoints("a\uD800\uDC00b", 3, -1); + assertEquals(1, result); + + result = Character.offsetByCodePoints("a\uD800\uDC00b", 3, 0); + assertEquals(3, result); + + result = Character.offsetByCodePoints("\uD800\uDC00bc", 3, 0); + assertEquals(3, result); + + result = Character.offsetByCodePoints("a\uDC00bc", 3, -1); + assertEquals(2, result); + + result = Character.offsetByCodePoints("a\uD800bc", 3, -1); + assertEquals(2, result); + + try { + Character.offsetByCodePoints((CharSequence) null, 0, 1); + fail(); + } catch (NullPointerException e) { + } + + try { + Character.offsetByCodePoints("abc", -1, 1); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abc", 4, 1); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abc", 1, 3); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abc", 1, -2); + fail(); + } catch (IndexOutOfBoundsException e) { + } + } + + public void test_offsetByCodePoints$CIIII() { + int result = Character.offsetByCodePoints("a\uD800\uDC00b" + .toCharArray(), 0, 4, 0, 2); + assertEquals(3, result); + + result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), + 0, 4, 0, 3); + assertEquals(4, result); + + result = Character.offsetByCodePoints("a\uD800\uDC00b\uD800c" + .toCharArray(), 0, 5, 0, 3); + assertEquals(4, result); + + result = Character + .offsetByCodePoints("abcd".toCharArray(), 0, 4, 3, -1); + assertEquals(2, result); + + result = Character + .offsetByCodePoints("abcd".toCharArray(), 1, 2, 3, -2); + assertEquals(1, result); + + result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), + 0, 4, 3, -1); + assertEquals(1, result); + + result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), + 0, 2, 2, -1); + assertEquals(1, result); + + result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), + 0, 4, 3, 0); + assertEquals(3, result); + + result = Character.offsetByCodePoints("\uD800\uDC00bc".toCharArray(), + 0, 4, 3, 0); + assertEquals(3, result); + + result = Character.offsetByCodePoints("a\uDC00bc".toCharArray(), 0, 4, + 3, -1); + assertEquals(2, result); + + result = Character.offsetByCodePoints("a\uD800bc".toCharArray(), 0, 4, + 3, -1); + assertEquals(2, result); + + try { + Character.offsetByCodePoints(null, 0, 4, 1, 1); + fail(); + } catch (NullPointerException e) { + } + + try { + Character.offsetByCodePoints("abcd".toCharArray(), -1, 4, 1, 1); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abcd".toCharArray(), 0, -1, 1, 1); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abcd".toCharArray(), 2, 4, 1, 1); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abcd".toCharArray(), 1, 3, 0, 1); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abcd".toCharArray(), 1, 1, 3, 1); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abc".toCharArray(), 0, 3, 1, 3); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abc".toCharArray(), 0, 2, 1, 2); + fail(); + } catch (IndexOutOfBoundsException e) { + } + + try { + Character.offsetByCodePoints("abc".toCharArray(), 1, 3, 1, -2); + fail(); + } catch (IndexOutOfBoundsException e) { + } + } +}