Fixes issues #490, #986, #1066. Implements a number of missing String and Character methods, adds and improves many of the tests for these classes, and adds a general-purpose method to hide constants from the compiler to make tests more resilient against compiler optimizations. Issues: 490, 986, 1066 Patch by: jat Review by: spoon git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2185 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/junit/client/GWTTestCase.java b/user/src/com/google/gwt/junit/client/GWTTestCase.java index d5d0feb..6ae89ce 100644 --- a/user/src/com/google/gwt/junit/client/GWTTestCase.java +++ b/user/src/com/google/gwt/junit/client/GWTTestCase.java
@@ -52,6 +52,7 @@ * traces. It can be useful for debugging web mode failures, but * production code should not depend on it. */ + @Deprecated public final void addCheckpoint(String msg) { // implemented in the translatable version of this class } @@ -78,6 +79,7 @@ * traces. It can be useful for debugging web mode failures, but * production code should not depend on it. */ + @Deprecated public final void clearCheckpoints() { // implemented in the translatable version of this class } @@ -91,6 +93,7 @@ * traces. It can be useful for debugging web mode failures, but * production code should not depend on it. */ + @Deprecated public final String[] getCheckpoints() { // implemented in the translatable version of this class return null; @@ -176,6 +179,23 @@ } /** + * Wrap a constant in such a way as to prevent the compiler from inlining it. + * + * NOTE: if you change this, update the one in gwt-user/core/super as well. + * + * @param <T> + * @param value to wrap + * @return same value, + */ + protected <T> T hideFromCompiler(T value) { + int i = 7; + while (i > 0) { + i -= 2; + } + return (i & 1) != 0 ? value : null; + } + + /** * Runs the test via the {@link JUnitShell} environment. Do not override or * call this method. */
diff --git a/user/super/com/google/gwt/emul/java/lang/Character.java b/user/super/com/google/gwt/emul/java/lang/Character.java index fbe8f5c..21ae92a 100644 --- a/user/super/com/google/gwt/emul/java/lang/Character.java +++ b/user/super/com/google/gwt/emul/java/lang/Character.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * 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 @@ -19,14 +19,80 @@ /** * Wraps a native <code>char</code> as an object. + * + * TODO(jat): many of the classification methods implemented here are not + * correct in that they only handle ASCII characters, and many other methods + * are not currently implemented. I think the proper approach is to introduce * a deferred binding parameter which substitutes an implementation using + * a fully-correct Unicode character database, at the expense of additional + * data being downloaded. That way developers that need the functionality + * can get it without those who don't need it paying for it. + * + * <pre> + * The following methods are still not implemented -- most would require Unicode + * character db to be useful: + * - digit / is* / to*(int codePoint) + * - isDefined(char) + * - isIdentifierIgnorable(char) + * - isJavaIdentifierPart(char) + * - isJavaIdentifierStart(char) + * - isJavaLetter(char) -- deprecated, so probably not + * - isJavaLetterOrDigit(char) -- deprecated, so probably not + * - isISOControl(char) + * - isMirrored(char) + * - isSpaceChar(char) + * - isTitleCase(char) + * - isUnicodeIdentifierPart(char) + * - isUnicodeIdentifierStart(char) + * - isWhitespace(char) + * - getDirectionality(*) + * - getNumericValue(*) + * - getType(*) + * - reverseBytes(char) -- any use for this at all in the browser? + * - toTitleCase(*) + * - all the category constants for classification + * + * The following do not properly handle characters outside of ASCII: + * - digit(char c, int radix) + * - isDigit(char c) + * - isLetter(char c) + * - isLetterOrDigit(char c) + * - isLowerCase(char c) + * - isUpperCase(char c) + * </pre> */ public final class Character implements Comparable<Character>, Serializable { + /** + * Helper class to share code between implementations, by making a char + * array look like a CharSequence. + */ + static class CharSequenceAdapter implements CharSequence { + private char[] charArray; + private int start; + private int end; - public static final int MIN_RADIX = 2; - public static final int MAX_RADIX = 36; + public CharSequenceAdapter(char[] charArray) { + this(charArray, 0, charArray.length); + } - public static final char MIN_VALUE = '\u0000'; - public static final char MAX_VALUE = '\uFFFF'; + public CharSequenceAdapter(char[] charArray, int start, int end) { + this.charArray = charArray; + this.start = start; + this.end = end; + } + + public char charAt(int index) { + return charArray[index + start]; + } + + public int length() { + return end - start; + } + + public java.lang.CharSequence subSequence(int start, int end) { + return new CharSequenceAdapter(charArray, this.start + start, + this.start + end); + } + } /** * Use nested class to avoid clinit on outer. @@ -36,6 +102,77 @@ private static Character[] boxedValues = new Character[128]; } + public static final Class<Character> TYPE = Character.class; + public static final int MIN_RADIX = 2; + + public static final int MAX_RADIX = 36; + public static final char MIN_VALUE = '\u0000'; + + public static final char MAX_VALUE = '\uFFFF'; + public static final char MIN_SURROGATE = '\uD800'; + public static final char MAX_SURROGATE = '\uDFFF'; + public static final char MIN_LOW_SURROGATE = '\uDC00'; + public static final char MAX_LOW_SURROGATE = '\uDFFF'; + public static final char MIN_HIGH_SURROGATE = '\uD800'; + + public static final char MAX_HIGH_SURROGATE = '\uDBFF'; + public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x10000; + public static final int MIN_CODE_POINT = 0x0000; + + public static final int MAX_CODE_POINT = 0x10FFFF; + + public static final int SIZE = 16; + + public static int charCount(int codePoint) { + return codePoint >= MIN_SUPPLEMENTARY_CODE_POINT ? 2 : 1; + } + + public static int codePointAt(char[] a, int index) { + return codePointAt(new CharSequenceAdapter(a), index, a.length); + } + + public static int codePointAt(char[] a, int index, int limit) { + return codePointAt(new CharSequenceAdapter(a), index, limit); + } + + public static int codePointAt(CharSequence seq, int index) { + return codePointAt(seq, index, seq.length()); + } + + public static int codePointBefore(char[] a, int index) { + return codePointBefore(new CharSequenceAdapter(a), index, 0); + } + + public static int codePointBefore(char[] a, int index, int start) { + return codePointBefore(new CharSequenceAdapter(a), index, start); + } + + public static int codePointBefore(CharSequence cs, int index) { + return codePointBefore(cs, index, 0); + } + + public static int codePointCount(char[] a, int offset, int count) { + return codePointCount(new CharSequenceAdapter(a), offset, offset + count); + } + + public static int codePointCount(CharSequence seq, int beginIndex, + int endIndex) { + int count = 0; + for (int idx = beginIndex; idx < endIndex; ) { + char ch = seq.charAt(idx++); + if (isHighSurrogate(ch) && idx < endIndex + && (isLowSurrogate(seq.charAt(idx)))) { + // skip the second char of surrogate pairs + ++idx; + } + ++count; + } + return count; + } + + /* + * TODO: correct Unicode handling. + */ public static int digit(char c, int radix) { if (radix < MIN_RADIX || radix > MAX_RADIX) { return -1; @@ -77,28 +214,52 @@ /** * @skip * - * Here for shared implementation with Arrays.hashCode + * public for shared implementation with Arrays.hashCode */ public static int hashCode(char c) { return c; } + /* + * TODO: correct Unicode handling. + */ public static native boolean isDigit(char c) /*-{ return (null != String.fromCharCode(c).match(/\d/)); }-*/; + public static boolean isHighSurrogate(char ch) { + return ch >= MIN_HIGH_SURROGATE && ch <= MAX_HIGH_SURROGATE; + } + + /* + * TODO: correct Unicode handling. + */ public static native boolean isLetter(char c) /*-{ return (null != String.fromCharCode(c).match(/[A-Z]/i)); }-*/; + /* + * TODO: correct Unicode handling. + */ public static native boolean isLetterOrDigit(char c) /*-{ return (null != String.fromCharCode(c).match(/[A-Z\d]/i)); }-*/; + /* + * TODO: correct Unicode handling. + */ public static boolean isLowerCase(char c) { return toLowerCase(c) == c && isLetter(c); } + public static boolean isLowSurrogate(char ch) { + return ch >= MIN_LOW_SURROGATE && ch <= MAX_LOW_SURROGATE; + } + + /** + * Deprecated - see isWhitespace(char). + */ + @Deprecated public static boolean isSpace(char c) { switch (c) { case ' ': @@ -116,9 +277,95 @@ } } + public static boolean isSupplementaryCodePoint(int codePoint) { + return codePoint >= MIN_SUPPLEMENTARY_CODE_POINT && codePoint <= MAX_CODE_POINT; + } + + public static boolean isSurrogatePair(char highSurrogate, char lowSurrogate) { + return isHighSurrogate(highSurrogate) && isLowSurrogate(lowSurrogate); + } + + /* + * TODO: correct Unicode handling. + */ public static boolean isUpperCase(char c) { return toUpperCase(c) == c && isLetter(c); } + + public static boolean isValidCodePoint(int codePoint) { + return codePoint >= MIN_CODE_POINT && codePoint <= MAX_CODE_POINT; + } + + public static int offsetByCodePoints(char[] a, int start, int count, int index, + int codePointOffset) { + return offsetByCodePoints(new CharSequenceAdapter(a, start, count), index, + codePointOffset); + } + + public static int offsetByCodePoints(CharSequence seq, int index, + int codePointOffset) { + if (codePointOffset < 0) { + // move backwards + while (codePointOffset < 0) { + --index; + if (Character.isLowSurrogate(seq.charAt(index)) + && Character.isHighSurrogate(seq.charAt(index - 1))) { + --index; + } + ++codePointOffset; + } + } else { + // move forwards + while (codePointOffset > 0) { + if (Character.isHighSurrogate(seq.charAt(index)) + && Character.isLowSurrogate(seq.charAt(index + 1))) { + ++index; + } + ++index; + --codePointOffset; + } + } + return index; + } + + public static char[] toChars(int codePoint) { + if (codePoint < 0 || codePoint > MAX_CODE_POINT) { + throw new IllegalArgumentException(); + } + if (codePoint >= MIN_SUPPLEMENTARY_CODE_POINT) { + return new char[] { + getHighSurrogate(codePoint), + getLowSurrogate(codePoint), + }; + } else { + return new char[] { + (char) codePoint, + }; + } + } + + public static int toChars(int codePoint, char[] dst, int dstIndex) { + if (codePoint < 0 || codePoint > MAX_CODE_POINT) { + throw new IllegalArgumentException(); + } + if (codePoint >= MIN_SUPPLEMENTARY_CODE_POINT) { + dst[dstIndex++] = getHighSurrogate(codePoint); + dst[dstIndex] = getLowSurrogate(codePoint); + return 2; + } else { + dst[dstIndex] = (char) codePoint; + return 1; + } + } + + public static int toCodePoint(char highSurrogate, char lowSurrogate) { + /* + * High and low surrogate chars have the bottom 10 bits to store the value + * above MIN_SUPPLEMENTARY_CODE_POINT, so grab those bits and add the + * offset. + */ + return MIN_SUPPLEMENTARY_CODE_POINT + ((highSurrogate & 1023) << 10) + (lowSurrogate & 1023); + } public static native char toLowerCase(char c) /*-{ return String.fromCharCode(c).toLowerCase().charCodeAt(0); @@ -143,6 +390,51 @@ return new Character(c); } + static int codePointAt(CharSequence cs, int index, int limit) { + char hiSurrogate = cs.charAt(index++); + char loSurrogate; + if (Character.isHighSurrogate(hiSurrogate) && index < limit + && Character.isLowSurrogate(loSurrogate = cs.charAt(index))) { + return Character.toCodePoint(hiSurrogate, loSurrogate); + } + return hiSurrogate; + } + + static int codePointBefore(CharSequence cs, int index, int start) { + char loSurrogate = cs.charAt(--index); + char highSurrogate; + if (isLowSurrogate(loSurrogate) && index > start + && isHighSurrogate(highSurrogate = cs.charAt(index - 1))) { + return toCodePoint(highSurrogate, loSurrogate); + } + return loSurrogate; + } + + /** + * Computes the high surrogate character of the UTF16 representation of a + * non-BMP code point. See {@link getLowSurrogate}. + * + * @param codePoint requested codePoint, required to be >= + * MIN_SUPPLEMENTARY_CODE_POINT + * @return high surrogate character + */ + static char getHighSurrogate(int codePoint) { + return (char) (MIN_HIGH_SURROGATE + + (((codePoint - MIN_SUPPLEMENTARY_CODE_POINT) >> 10) & 1023)); + } + + /** + * Computes the low surrogate character of the UTF16 representation of a + * non-BMP code point. See {@link getHighSurrogate}. + * + * @param codePoint requested codePoint, required to be >= + * MIN_SUPPLEMENTARY_CODE_POINT + * @return low surrogate character + */ + static char getLowSurrogate(int codePoint) { + return (char) (MIN_LOW_SURROGATE + ((codePoint - MIN_SUPPLEMENTARY_CODE_POINT) & 1023)); + } + private final transient char value; public Character(char value) { @@ -154,13 +446,8 @@ } public int compareTo(Character c) { - if (value < c.value) { - return -1; - } else if (value > c.value) { - return 1; - } else { - return 0; - } + // JLS specifies that the chars are promoted to int before subtraction. + return value - c.value; } @Override
diff --git a/user/super/com/google/gwt/emul/java/lang/String.java b/user/super/com/google/gwt/emul/java/lang/String.java index cd2464b..197e5cb 100644 --- a/user/super/com/google/gwt/emul/java/lang/String.java +++ b/user/super/com/google/gwt/emul/java/lang/String.java
@@ -25,13 +25,63 @@ import com.google.gwt.core.client.JavaScriptObject; import java.io.Serializable; +import java.util.Comparator; /** * Intrinsic string class. + * + * TODO(jat): consider whether we want to support the following methods; + * + * <ul> + * <li>deprecated methods dealing with bytes (I assume not since I can't see much use for them) + * <ul> + * <li>String(byte[] ascii, int hibyte) + * <li>String(byte[] ascii, int hibyte, int offset, int count) + * <li>getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin) + * </ul> + * <li>methods which in JS will essentially do nothing or be the same as other methods + * <ul> + * <li>copyValueOf(char[] data) + * <li>copyValueOf(char[] data, int offset, int count) + * </ul> + * <li>methods added in Java 1.6 (the issue is how will it impact users building against Java 1.5) + * <ul> + * <li>isEmpty() + * </ul> + * <li>other methods which are not straightforward in JS + * <ul> + * <li>format(String format, Object... args) + * </ul> + * </ul> + * + * Also, in general, we need to improve our support of non-ASCII characters. The problem is + * that correct support requires large tables, and we don't want to make users who aren't going + * to use that pay for it. There are two ways to do that: + * <ol> + * <li>construct the tables in such a way that if the corresponding method is not called the + * table will be elided from the output. + * <li>provide a deferred binding target selecting the level of compatibility required. Those + * that only need ASCII (or perhaps a different relatively small subset such as Latin1-5) + * will not pay for large tables, even if they do call toLowercase(), for example. + * </ol> + * + * Also, if we ever add multi-locale support, there are a number of other methods such as + * toLowercase(Locale) we will want to consider supporting. This is probably rare, but there + * will be some apps (such as a translation tool) which cannot be written without this support. + * + * Another category of incomplete support is that we currently just use the JS regex support, + * which is not exactly the same as Java. We should support Java syntax by mapping it into + * equivalent JS patterns, or emulating them. */ -public final class String implements Comparable<String>, CharSequence, +public final class String implements Comparable<String>, CharSequence, Serializable { + public static final Comparator<String> CASE_INSENSITIVE_ORDER = new Comparator<String>() { + public int compare(String a, String b) { + return a.compareToIgnoreCase(b); + } + }; + /** * Accesses need to be prefixed with ':' to prevent conflict with built-in * JavaScript properties. @@ -63,7 +113,7 @@ public static String valueOf(double x) { return "" + x; } - + public static String valueOf(float x) { return "" + x; } @@ -161,6 +211,32 @@ /** * @skip */ + static String _String(int[] codePoints, int offset, int count) { + char[] chars = new char[count * 2]; + int charIdx = 0; + while (count-- > 0) { + charIdx += Character.toChars(codePoints[offset++], chars, charIdx); + } + return valueOf(chars, 0, charIdx); + } + + /** + * @skip + */ + static String _String(StringBuffer sb) { + return valueOf(sb); + } + + /** + * @skip + */ + static String _String(StringBuilder sb) { + return valueOf(sb); + } + + /** + * @skip + */ static String _String(String other) { return other; } @@ -172,6 +248,52 @@ // CHECKSTYLE_ON + private static native int compareTo(String thisStr, String otherStr) /*-{ + // Coerce to a primitive string to force string comparison + thisStr = String(thisStr); + if (thisStr == otherStr) { + return 0; + } + return thisStr < otherStr ? -1 : 1; + }-*/; + + private static native String fromCharCode(char ch) /*-{ + return String.fromCharCode(ch); + }-*/; + + private static String fromCodePoint(int codePoint) { + if (codePoint >= Character.MIN_SUPPLEMENTARY_CODE_POINT) { + char hiSurrogate = Character.getHighSurrogate(codePoint); + char loSurrogate = Character.getLowSurrogate(codePoint); + return String.fromCharCode(hiSurrogate) + + String.fromCharCode(loSurrogate); + } else { + return String.fromCharCode((char) codePoint); + } + } + + private static native boolean regionMatches(String thisStr, boolean ignoreCase, int toffset, + String other, int ooffset, int len) /*-{ + if (toffset < 0 || ooffset < 0 || len <= 0) { + return false; + } + + if (toffset + len > thisStr.length || ooffset + len > other.length) { + return false; + } + + var left = thisStr.substr(toffset, len); + var right = other.substr(ooffset, len); + + if (ignoreCase) { + left = left.toLowerCase(); + right = right.toLowerCase(); + } + + return left == right; + }-*/; + + public String() { // magic delegation to _String _String(); @@ -187,30 +309,48 @@ _String(value, offset, count); } + public String(int codePoints[], int offset, int count) { + // magic delegation to _String + _String(codePoints, offset, count); + } + public String(String other) { // magic delegation to _String _String(other); } + public String(StringBuffer sb) { + // magic delegation to _String + _String(sb); + } + + public String(StringBuilder sb) { + // magic delegation to _String + _String(sb); + } + public native char charAt(int index) /*-{ return this.charCodeAt(index); }-*/; + public int codePointAt(int index) { + return Character.codePointAt(this, index, length()); + } + + public int codePointBefore(int index) { + return Character.codePointBefore(this, index, 0); + } + + public int codePointCount(int beginIndex, int endIndex) { + return Character.codePointCount(this, beginIndex, endIndex); + } + public int compareTo(String other) { - if (__equals(this, other)) { - return 0; - } - int thisLength = this.length(); - int otherLength = other.length(); - int length = Math.min(thisLength, otherLength); - for (int i = 0; i < length; i++) { - char thisChar = this.charAt(i); - char otherChar = other.charAt(i); - if (thisChar != otherChar) { - return thisChar - otherChar; - } - } - return thisLength - otherLength; + return compareTo(this, other); + } + + public int compareToIgnoreCase(String other) { + return compareTo(toLowerCase(), other.toLowerCase()); } public native String concat(String str) /*-{ @@ -221,6 +361,14 @@ return indexOf(s.toString()) != -1; } + public boolean contentEquals(CharSequence cs) { + return equals(cs.toString()); + } + + public boolean contentEquals(StringBuffer sb) { + return equals(sb.toString()); + } + public native boolean endsWith(String suffix) /*-{ return (this.lastIndexOf(suffix) != -1) && (this.lastIndexOf(suffix) == (this.length - suffix.length)); @@ -240,6 +388,12 @@ return (this == other) || (this.toLowerCase() == other.toLowerCase()); }-*/; + public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { + for (int srcIdx = srcBegin; srcIdx < srcEnd; ++srcIdx) { + dst[dstBegin++] = charAt(srcIdx); + } + } + @Override public native int hashCode() /*-{ var hashCache = @java.lang.String::hashCache; @@ -272,13 +426,13 @@ return hashCode; }-*/; - public native int indexOf(int ch) /*-{ - return this.indexOf(String.fromCharCode(ch)); - }-*/; + public int indexOf(int codePoint) { + return indexOf(fromCodePoint(codePoint)); + } - public native int indexOf(int ch, int startIndex) /*-{ - return this.indexOf(String.fromCharCode(ch), startIndex); - }-*/; + public int indexOf(int codePoint, int startIndex) { + return this.indexOf(String.fromCodePoint(codePoint), startIndex); + } public native int indexOf(String str) /*-{ return this.indexOf(str); @@ -288,13 +442,17 @@ return this.indexOf(str, startIndex); }-*/; - public native int lastIndexOf(int ch) /*-{ - return this.lastIndexOf(String.fromCharCode(ch)); + public native String intern() /*-{ + return String(this); }-*/; - public native int lastIndexOf(int ch, int startIndex) /*-{ - return this.lastIndexOf(String.fromCharCode(ch), startIndex); - }-*/; + public int lastIndexOf(int codePoint) { + return lastIndexOf(fromCodePoint(codePoint)); + } + + public int lastIndexOf(int codePoint, int startIndex) { + return lastIndexOf(fromCodePoint(codePoint), startIndex); + } public native int lastIndexOf(String str) /*-{ return this.lastIndexOf(str); @@ -313,6 +471,8 @@ * <code>regex</code> parameter is interpreted by JavaScript as a JavaScript * regular expression. For consistency, use only the subset of regular * expression syntax common to both Java and JavaScript. + * + * TODO(jat): properly handle Java regex syntax */ public native boolean matches(String regex) /*-{ var matchObj = new RegExp(regex).exec(this); @@ -321,6 +481,25 @@ return (matchObj == null) ? false : (this == matchObj[0]); }-*/; + public int offsetByCodePoints(int index, int codePointOffset) { + return Character.offsetByCodePoints(this, index, codePointOffset); + } + + public boolean regionMatches(boolean ignoreCase, int toffset, String other, + int ooffset, int len) { + if (other == null) { + throw new NullPointerException(); + } + return regionMatches(this, ignoreCase, toffset, other, ooffset, len); + } + + public boolean regionMatches(int toffset, String other, int ooffset, int len) { + if (other == null) { + throw new NullPointerException(); + } + return regionMatches(this, false, toffset, other, ooffset, len); + } + public native String replace(char from, char to) /*-{ // We previously used \\uXXXX, but Safari 2 doesn't match them properly in RegExp // See http://bugs.webkit.org/show_bug.cgi?id=8043 @@ -337,11 +516,20 @@ return this.replace(RegExp(regex, "g"), String.fromCharCode(to)); }-*/; + public String replace(CharSequence from, CharSequence to) { + // Escape regex special characters from literal replacement string. + String regex = from.toString().replaceAll("([/\\\\\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}])", + "\\\\$1"); + return replaceAll(regex, to.toString()); + } + /** * Regular expressions vary from the standard implementation. The * <code>regex</code> parameter is interpreted by JavaScript as a JavaScript * regular expression. For consistency, use only the subset of regular * expression syntax common to both Java and JavaScript. + * + * TODO(jat): properly handle Java regex syntax */ public native String replaceAll(String regex, String replace) /*-{ replace = @java.lang.String::__translateReplaceString(Ljava/lang/String;)(replace); @@ -353,6 +541,8 @@ * <code>regex</code> parameter is interpreted by JavaScript as a JavaScript * regular expression. For consistency, use only the subset of regular * expression syntax common to both Java and JavaScript. + * + * TODO(jat): properly handle Java regex syntax */ public native String replaceFirst(String regex, String replace) /*-{ replace = @java.lang.String::__translateReplaceString(Ljava/lang/String;)(replace); @@ -374,6 +564,8 @@ * <code>regex</code> parameter is interpreted by JavaScript as a JavaScript * regular expression. For consistency, use only the subset of regular * expression syntax common to both Java and JavaScript. + * + * TODO(jat): properly handle Java regex syntax */ public native String[] split(String regex, int maxMatch) /*-{ // The compiled regular expression created from the string @@ -449,15 +641,13 @@ }-*/; public native String substring(int beginIndex, int endIndex) /*-{ - return this.substr(beginIndex, endIndex-beginIndex); + return this.substr(beginIndex, endIndex - beginIndex); }-*/; public char[] toCharArray() { int n = this.length(); char[] charArr = new char[n]; - for (int i = 0; i < n; ++i) { - charArr[i] = this.charAt(i); - } + getChars(0, n, charArr, 0); return charArr; }
diff --git a/user/super/com/google/gwt/junit/translatable/com/google/gwt/junit/client/GWTTestCase.java b/user/super/com/google/gwt/junit/translatable/com/google/gwt/junit/client/GWTTestCase.java index d3adc70..973def6 100644 --- a/user/super/com/google/gwt/junit/translatable/com/google/gwt/junit/client/GWTTestCase.java +++ b/user/super/com/google/gwt/junit/translatable/com/google/gwt/junit/client/GWTTestCase.java
@@ -221,6 +221,23 @@ "This test case does not support asynchronous mode."); } } + + /** + * Wrap a constant in such a way as to prevent the compiler from inlining it. + * + * NOTE: if you change this, update the one in gwt-user/core/src as well. + * + * @param <T> + * @param value to wrap + * @return same value, + */ + protected <T> T hideFromCompiler(T value) { + int i = 7; + while (i > 0) { + i -= 2; + } + return (i & 1) != 0 ? value : null; + } protected boolean supportsAsync() { return true;
diff --git a/user/test/com/google/gwt/emultest/java/lang/CharacterTest.java b/user/test/com/google/gwt/emultest/java/lang/CharacterTest.java index 367c71f..c8a615c 100644 --- a/user/test/com/google/gwt/emultest/java/lang/CharacterTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/CharacterTest.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * Copyright 2008 Google Inc. * * 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 @@ -18,12 +18,43 @@ import com.google.gwt.junit.client.GWTTestCase; /** - * TODO: document me. + * Tests for java.lang.Character. */ public class CharacterTest extends GWTTestCase { + private static class CharSequenceAdapter implements CharSequence { + private char[] charArray; + private int start; + private int end; + + public CharSequenceAdapter(char[] charArray) { + this(charArray, 0, charArray.length); + } + + public CharSequenceAdapter(char[] charArray, int start, int end) { + this.charArray = charArray; + this.start = start; + this.end = end; + } + + public char charAt(int index) { + return charArray[index + start]; + } + + public int length() { + return end - start; + } + + public java.lang.CharSequence subSequence(int start, int end) { + return new CharSequenceAdapter(charArray, this.start + start, + this.start + end); + } + } + + /** - * TODO: document me. + * Helper class which applies some arbitrary char mutation function + * to a string and returns it. */ public abstract class Changer { String original; @@ -43,7 +74,8 @@ } } /** - * TODO: document me. + * Helper class which collects the set of characters which pass some + * arbitrary boolean function. */ public abstract class Judge { String original; @@ -119,8 +151,9 @@ }; Judge lowerCaseJudge = new LowerCaseJudge(allChars); Judge spaceJudge = new Judge(allChars) { + @SuppressWarnings("deprecation") // Character.isSpace() public boolean pass(char c) { - return Character.isSpace(c); + return Character.isSpace(c); // suppress deprecation } }; Changer upperCaseChanger = new Changer(allChars) { @@ -138,6 +171,77 @@ assertEquals(new Character((char) 32).charValue(), (char) 32); } + public void testCodePoint() { + assertEquals(1, Character.charCount(65)); + assertEquals(2, Character.charCount(Character.MIN_SUPPLEMENTARY_CODE_POINT)); + char[] testPlain = new char[] { 'C', 'A', 'T' }; + char[] testUnicode = new char[] { 'C', '\uD801', '\uDF00', 'T' }; + CharSequence plainSequence = new CharSequenceAdapter(testPlain); + CharSequence unicodeSequence = new CharSequenceAdapter(testUnicode); + assertEquals(65, Character.codePointAt(testPlain, 1)); + assertEquals(65, Character.codePointAt(plainSequence, 1)); + assertEquals("codePointAt fails on surrogate pair", 67328, + Character.codePointAt(testUnicode, 1)); + assertEquals("codePointAt fails on surrogate pair", 67328, + Character.codePointAt(unicodeSequence, 1)); + assertEquals("codePointAt fails on first char of surrogate pair", 0xD801, + Character.codePointAt(testUnicode, 1, 2)); + assertEquals(65, Character.codePointBefore(testPlain, 2)); + assertEquals(65, Character.codePointBefore(plainSequence, 2)); + assertEquals("codePointBefore fails on surrogate pair", 67328, + Character.codePointBefore(testUnicode, 3)); + assertEquals("codePointBefore fails on surrogate pair", 67328, + Character.codePointBefore(unicodeSequence, 3)); + assertEquals("codePointBefore fails on second char of surrogate pair", + 0xDF00, Character.codePointBefore(testUnicode, 3, 2)); + assertEquals("codePointCount(plain): ", 3, + Character.codePointCount(testPlain, 0, 3)); + assertEquals("codePointCount(plain): ", 3, + Character.codePointCount(plainSequence, 0, 3)); + assertEquals("codePointCount(unicode): ", 3, + Character.codePointCount(testUnicode, 0, 4)); + assertEquals("codePointCount(unicode): ", 3, + Character.codePointCount(unicodeSequence, 0, 4)); + assertEquals(1, Character.codePointCount(testPlain, 1, 1)); + assertEquals(1, Character.codePointCount(plainSequence, 1, 2)); + assertEquals(1, Character.codePointCount(testUnicode, 1, 2)); + assertEquals(1, Character.codePointCount(unicodeSequence, 1, 3)); + assertEquals(2, Character.codePointCount(testUnicode, 2, 2)); + assertEquals(2, Character.codePointCount(unicodeSequence, 2, 4)); + assertEquals(1, Character.offsetByCodePoints(testUnicode, 0, 4, 0, 1)); + assertEquals(1, Character.offsetByCodePoints(unicodeSequence, 0, 1)); + assertEquals("offsetByCodePoints(1,1): ", 3, + Character.offsetByCodePoints(testUnicode, 0, 4, 1, 1)); + assertEquals("offsetByCodePoints(1,1): ", 3, + Character.offsetByCodePoints(unicodeSequence, 1, 1)); + assertEquals("offsetByCodePoints(2,1): ", 3, + Character.offsetByCodePoints(testUnicode, 0, 4, 2, 1)); + assertEquals("offsetByCodePoints(2,1): ", 3, + Character.offsetByCodePoints(unicodeSequence, 2, 1)); + assertEquals(4, Character.offsetByCodePoints(testUnicode, 0, 4, 3, 1)); + assertEquals(4, Character.offsetByCodePoints(unicodeSequence, 3, 1)); + assertEquals(1, Character.offsetByCodePoints(testUnicode, 0, 4, 2, -1)); + assertEquals(1, Character.offsetByCodePoints(unicodeSequence, 2, -1)); + assertEquals(1, Character.offsetByCodePoints(testUnicode, 0, 4, 3, -1)); + assertEquals(1, Character.offsetByCodePoints(unicodeSequence, 3, -1)); + assertEquals("offsetByCodePoints(4.-1): ", 3, + Character.offsetByCodePoints(testUnicode, 0, 4, 4, -1)); + assertEquals("offsetByCodePoints(4.-1): ", 3, + Character.offsetByCodePoints(unicodeSequence, 4, -1)); + assertEquals(0, Character.offsetByCodePoints(testUnicode, 0, 4, 3, -2)); + assertEquals(0, Character.offsetByCodePoints(unicodeSequence, 3, -2)); + char[] nonBmpChar = new char[] { '\uD800', '\uDF46' }; + assertEquals(0x10346, Character.codePointAt(nonBmpChar, 0)); + assertEquals(1, Character.codePointCount(nonBmpChar, 0, 2)); + } + + public void testCompareTo() { + assertTrue(Character.valueOf('A').compareTo('B') < 0); + assertTrue(Character.valueOf('B').compareTo('A') > 0); + assertTrue(Character.valueOf('C').compareTo('C') == 0); + assertTrue(Character.valueOf('\uA001').compareTo('\uA000') > 0); + } + public void testConstructor() { assertEquals(new Character((char) 32), new Character(' ')); } @@ -145,6 +249,33 @@ public void testDigit() { assertEquals("wrong number of digits", 10, digitJudge.allPass().length()); } + + public void testSurrogates() { + assertFalse(Character.isHighSurrogate('\uDF46')); + assertTrue(Character.isLowSurrogate('\uDF46')); + assertTrue(Character.isHighSurrogate('\uD800')); + assertFalse(Character.isLowSurrogate('\uD800')); + assertFalse(Character.isHighSurrogate('X')); + assertFalse(Character.isLowSurrogate('X')); + assertTrue(Character.isSurrogatePair('\uD800', '\uDF46')); + assertFalse(Character.isSurrogatePair('\uDF46', '\uD800')); + assertFalse(Character.isSurrogatePair('A', '\uDF46')); + assertFalse(Character.isSurrogatePair('\uD800', 'A')); + char[] chars = Character.toChars(0x10346); + assertEquals(0xD800, chars[0]); + assertEquals(0xDF46, chars[1]); + assertEquals(2, Character.toChars(67328, chars, 0)); + assertEquals(0xD801, chars[0]); + assertEquals(0xDF00, chars[1]); + assertEquals(1, Character.toChars(65, chars, 0)); + assertEquals('A', chars[0]); + assertTrue(Character.isSupplementaryCodePoint(0x10346)); + assertFalse(Character.isSupplementaryCodePoint(65)); + assertTrue(Character.isValidCodePoint(0x10346)); + assertTrue(Character.isValidCodePoint(65)); + assertFalse(Character.isValidCodePoint(0x1FFFFFFF)); + assertEquals(0x10346, Character.toCodePoint('\uD800', '\uDF46')); + } public void testLetter() { assertEquals("wrong number of letters", 52, letterJudge.allPass().length()); @@ -178,7 +309,7 @@ } public void testToString() { - assertEquals(new Character((char) 32).toString(), " "); + assertEquals(" ", new Character((char) 32).toString()); } public void testUpperCase() { @@ -187,4 +318,8 @@ assertEquals("wrong number of uppercase letters after toUpperCase", 52, new UpperCaseJudge(upperCaseChanger.changed()).allPass().length()); } + + public void testValueOf() { + assertEquals('A', Character.valueOf('A').charValue()); + } }
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringTest.java b/user/test/com/google/gwt/emultest/java/lang/StringTest.java index 46430af..53fc23a 100644 --- a/user/test/com/google/gwt/emultest/java/lang/StringTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
@@ -20,43 +20,114 @@ /** * TODO: COMPILER OPTIMIZATIONS HAVE MADE THIS TEST NOT ACTUALLY TEST ANYTHING! * NEED A VERSION THAT DOESN'T USE STATICALLY DETERMINABLE STRINGS! + * + * See individual method TODOs for ones that still need work -- the ones without + * comments are already protected against optimization. */ public class StringTest extends GWTTestCase { + @Override public String getModuleName() { return "com.google.gwt.emultest.EmulSuite"; } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testCharAt() { assertEquals("abc".charAt(1), 'b'); } + public void testCodePoint() { + String testPlain = hideFromCompiler("CAT"); + String testUnicode = hideFromCompiler("C\uD801\uDF00T"); + assertEquals("CAT", new String(new int[] { 'C', 'A', 'T' }, 0, 3)); + assertEquals("C\uD801\uDF00T", new String(new int[] { 'C', 67328, 'T' }, 0, 3)); + assertEquals("\uD801\uDF00", new String(new int[] { 'C', 67328, 'T' }, 1, 1)); + assertEquals(65, testPlain.codePointAt(1)); + assertEquals("codePointAt fails on surrogate pair", 67328, testUnicode.codePointAt(1)); + assertEquals(65, testPlain.codePointBefore(2)); + assertEquals("codePointBefore fails on surrogate pair", 67328, testUnicode.codePointBefore(3)); + assertEquals("codePointCount(plain): ", 3, testPlain.codePointCount(0, 3)); + assertEquals("codePointCount(unicode): ", 3, testUnicode.codePointCount(0, 4)); + assertEquals(1, testPlain.codePointCount(1, 2)); + assertEquals(1, testUnicode.codePointCount(1, 2)); + assertEquals(2, testUnicode.codePointCount(2, 4)); + assertEquals(1, testUnicode.offsetByCodePoints(0, 1)); + assertEquals("offsetByCodePoints(1,1): ", 3, testUnicode.offsetByCodePoints(1, 1)); + assertEquals("offsetByCodePoints(2,1): ", 3, testUnicode.offsetByCodePoints(2, 1)); + assertEquals(4, testUnicode.offsetByCodePoints(3, 1)); + assertEquals(1, testUnicode.offsetByCodePoints(2, -1)); + assertEquals(1, testUnicode.offsetByCodePoints(3, -1)); + assertEquals("offsetByCodePoints(4.-1): ", 3, testUnicode.offsetByCodePoints(4, -1)); + assertEquals(0, testUnicode.offsetByCodePoints(3, -2)); + /* + * The next line contains a Unicode character outside the base multilingual + * plane -- it may not show properly depending on your fonts, etc. The + * character is the Gothic letter Faihu, or U+10346. We use it to verify + * that multi-char UTF16 characters are handled properly. + * + * In Windows 2000, registry changes are required to support non-BMP characters + * (or surrogates in general) -- surrogates are not supported before Win2k and + * they are enabled by default in WinXP and later. + * + * [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\LanguagePack] + * SURROGATE=(REG_DWORD)0x00000002 + * + * [HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\International\Scripts\42] + * IEFixedFontName=[Surrogate Font Face Name] + * IEPropFontName=[Surrogate Font Face Name] + */ + String nonBmpChar = hideFromCompiler("𐍆"); + assertEquals("\uD800\uDF46", nonBmpChar); + assertEquals(0x10346, nonBmpChar.codePointAt(0)); + assertEquals(2, nonBmpChar.length()); + assertEquals(1, nonBmpChar.codePointCount(0, 2)); + } + public void testConcat() { - assertEquals("abcdef", "abc" + "def"); - assertEquals("abcdef", "abc".concat("def")); - assertEquals("".concat(""), ""); - char c = 'd'; - String s = "abc"; - assertEquals("abcd", "abc" + 'd'); - assertEquals("abcd", "abc" + c); + String abc = String.valueOf(new char[] {'a', 'b', 'c'}); + String def = String.valueOf(new char[] {'d', 'e', 'f'}); + String empty = String.valueOf(new char[] {}); + assertEquals("abcdef", abc + def); + assertEquals("abcdef", abc.concat(def)); + assertEquals("", empty.concat(empty)); + char c = def.charAt(0); + String s = abc; + assertEquals("abcd", abc + 'd'); + assertEquals("abcd", abc + c); assertEquals("abcd", s + 'd'); assertEquals("abcd", s + c); s += c; assertEquals("abcd", s); } - + public void testConstructor() { char[] chars = {'a', 'b', 'c', 'd', 'e', 'f'}; - String constant = "abcdef"; - String shortString = "cde"; - assertEquals(new String(constant), constant); - assertEquals(new String(chars), constant); - assertEquals(new String(chars, 2, 3), shortString); - assertEquals(new String(""), ""); - assertEquals(new String(new String(new String(new String("")))), ""); - assertEquals(new String(new char[] {}), ""); + String constant = String.valueOf(new char[] {'a', 'b', 'c', 'd', 'e', 'f'}); + String shortString = String.valueOf(new char[] {'c', 'd', 'e'}); + assertEquals(constant, new String(hideFromCompiler(constant))); + assertEquals(constant, new String(chars), constant); + assertEquals(shortString, new String(chars, 2, 3), shortString); + assertEquals("", new String(hideFromCompiler(""))); + assertEquals("", new String(new String(new String(new String(hideFromCompiler("")))))); + assertEquals("", new String(new char[] {})); + StringBuffer buf = new StringBuffer(); + buf.append('c'); + buf.append('a'); + buf.append('t'); + assertEquals("cat", new String(buf)); + StringBuilder sb = new StringBuilder(); + sb.append('c'); + sb.append('a'); + sb.append('t'); + assertEquals("cat", new String(sb)); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + * (StringBuffer tests are ok) + */ public void testContains() { // at the beginning assertTrue("abcdef".contains("ab")); @@ -82,6 +153,9 @@ assertFalse("c", haystack.endsWith(haystack + "j")); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testEquals() { assertFalse("ABC".equals("abc")); assertFalse("abc".equals("ABC")); @@ -93,6 +167,9 @@ assertFalse("".equals(null)); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testEqualsIgnoreCase() { assertTrue("ABC".equalsIgnoreCase("abc")); assertTrue("abc".equalsIgnoreCase("ABC")); @@ -149,28 +226,44 @@ assertEquals(haystack.indexOf(""), 0); } + public void testIntern() { + String s1 = String.valueOf(new char[] {'a', 'b', 'c', 'd', 'e', 'f'}); + String s2 = String.valueOf(new char[] {'a', 'b', 'c', 'd', 'e', 'f'}); + assertTrue("strings not equal", s1.equals(s2)); + assertSame("interns are not the same reference", s1.intern(), s2.intern()); + } + public void testLastIndexOf() { String x = "abcdeabcdef"; assertEquals(9, x.lastIndexOf("e")); assertEquals(10, x.lastIndexOf("f")); assertEquals(-1, x.lastIndexOf("f", 1)); } - + public void testLength() { - assertEquals(3, "abc".length()); + String abc = String.valueOf(new char[] {'a', 'b', 'c'}); + assertEquals(3, abc.length()); String str = "x"; for (int i = 0; i < 16; i++) { str = str + str; } assertEquals(1 << 16, str.length()); + String cat = String.valueOf(new char[] {'C', '\uD801', '\uDF00', 'T'}); + assertEquals(4, cat.length()); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testLowerCase() { assertEquals("abc", "AbC".toLowerCase()); assertEquals("abc", "abc".toLowerCase()); assertEquals("", "".toLowerCase()); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testMatch() { assertFalse("1f", "abbbbcd".matches("b*")); assertFalse("2f", "abbbbcd".matches("b+")); @@ -182,15 +275,41 @@ assertFalse("8f", "abbbbcd".matches("a.*e")); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testNull() { assertNull(returnNull()); String a = returnNull() + returnNull(); assertEquals("nullnull", a); } + public void testRegionMatches() { + String test = String.valueOf(new char[] {'a', 'b', 'c', 'd', 'e', 'f'}); + assertTrue(test.regionMatches(1, "bcd", 0, 3)); + assertTrue(test.regionMatches(1, "bcdx", 0, 3)); + assertFalse(test.regionMatches(1, "bcdx", 0, 4)); + assertFalse(test.regionMatches(1, "bcdx", 1, 3)); + assertTrue(test.regionMatches(true, 0, "XAbCd", 1, 4)); + assertTrue(test.regionMatches(true, 1, "BcD", 0, 3)); + assertTrue(test.regionMatches(true, 1, "bCdx", 0, 3)); + assertFalse(test.regionMatches(true, 1, "bCdx", 0, 4)); + assertFalse(test.regionMatches(true, 1, "bCdx", 1, 3)); + assertTrue(test.regionMatches(true, 0, "xaBcd", 1, 4)); + test = test.toUpperCase(); + assertTrue(test.regionMatches(true, 0, "XAbCd", 1, 4)); + assertTrue(test.regionMatches(true, 1, "BcD", 0, 3)); + assertTrue(test.regionMatches(true, 1, "bCdx", 0, 3)); + assertFalse(test.regionMatches(true, 1, "bCdx", 0, 4)); + assertFalse(test.regionMatches(true, 1, "bCdx", 1, 3)); + assertTrue(test.regionMatches(true, 0, "xaBcd", 1, 4)); + } + public void testReplace() { - assertEquals("axax".replace('x', 'a'), "aaaa"); - assertEquals("aaaa".replace('x', 'a'), "aaaa"); + String axax = String.valueOf(new char[] {'a', 'x', 'a', 'x'}); + String aaaa = String.valueOf(new char[] {'a', 'a', 'a', 'a'}); + assertEquals("aaaa", axax.replace('x', 'a')); + assertEquals("aaaa", aaaa.replace('x', 'a')); for (char from = 32; from < 250; ++from) { char to = (char) (from + 5); assertEquals(toS(to), toS(from).replace(from, to)); @@ -200,19 +319,37 @@ assertEquals(toS(to), toS(from).replace(from, to)); } // issue 1480 - assertEquals("example xd", "example xd".replace('\r', ' ').replace('\n', ' ')); - assertEquals("dog food", "dog\u0120food".replace('\u0120', ' ')); - assertEquals("ABABAB", "\u1111B\u1111B\u1111B".replace('\u1111', 'A')); + String exampleXd + = String.valueOf(new char[] {'e', 'x', 'a', 'm', 'p', 'l', 'e', ' ', 'x', 'd'}); + assertEquals("example xd", exampleXd.replace('\r', ' ').replace('\n', ' ')); + String dogFood = String.valueOf(new char[] {'d', 'o', 'g', '\u0120', 'f', 'o', 'o', 'd'}); + assertEquals("dog food", dogFood.replace('\u0120', ' ')); + String testStr = String.valueOf(new char[] {'\u1111', 'B', '\u1111', 'B', '\u1111', 'B'}); + assertEquals("ABABAB", testStr.replace('\u1111', 'A')); + assertEquals("foobar", hideFromCompiler("bazbar").replace("baz", "foo")); + assertEquals("$0bar", hideFromCompiler("foobar").replace("foo", "$0")); + assertEquals("+1", hideFromCompiler("*[)1").replace("*[)", "+")); } public void testReplaceAll() { - assertEquals("abcdef", "xxxxabcxxdexf".replaceAll("x*", "")); - assertEquals("1\\1abc123\\123de1234\\1234f", "1abc123de1234f".replaceAll( + String regex = hideFromCompiler("*[").replaceAll("([/\\\\\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}])", + "\\\\$1"); + assertEquals("\\*\\[", regex); + assertEquals("+1", hideFromCompiler("*[1").replaceAll(regex, "+")); + String x1 = String.valueOf(new char[] {'x', 'x', 'x', 'a', 'b', 'c', 'x', 'x', 'd', 'e', 'x', + 'f'}); + assertEquals("abcdef", x1.replaceAll("x*", "")); + String x2 = String.valueOf(new char[] {'1', 'a', 'b', 'c', '1', '2', '3', 'd', 'e', '1', '2', + '3', '4', 'f'}); + assertEquals("1\\1abc123\\123de1234\\1234f", x2.replaceAll( "([1234]+)", "$1\\\\$1")); - assertEquals("\n \n", "x x".replaceAll("x", "\n")); - assertEquals("x x", "\n \n".replaceAll("\\\n", "x")); - assertEquals("x\"\\", "x".replaceAll("x", "\\x\\\"\\\\")); - assertEquals("$$x$", "x".replaceAll("(x)", "\\$\\$$1\\$")); + String x3 = String.valueOf(new char[] {'x', ' ', ' ', 'x'}); + assertEquals("\n \n", x3.replaceAll("x", "\n")); + String x4 = String.valueOf(new char[] {'\n', ' ', ' ', '\n'}); + assertEquals("x x", x4.replaceAll("\\\n", "x")); + String x5 = String.valueOf(new char[] {'x'}); + assertEquals("x\"\\", x5.replaceAll("x", "\\x\\\"\\\\")); + assertEquals("$$x$", x5.replaceAll("(x)", "\\$\\$$1\\$")); } public void testSplit() { @@ -245,6 +382,9 @@ assertFalse(haystack.startsWith(haystack + "j")); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testSubstring() { String haystack = "abcdefghi"; assertEquals("cd", haystack.substring(2, 4)); @@ -260,6 +400,9 @@ } } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testTrim() { trimRightAssertEquals("abc", " \t abc \n "); trimRightAssertEquals("abc", "abc".trim()); @@ -272,12 +415,18 @@ trimRightAssertEquals("", " \t ".trim()); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testUpperCase() { assertEquals("abc", "AbC".toLowerCase()); assertEquals("abc", "abc".toLowerCase()); assertEquals("", "".toLowerCase()); } + /* + * TODO: needs rewriting to avoid compiler optimizations. + */ public void testValueOf() { assertTrue(String.valueOf(C.FLOAT_VALUE).startsWith(C.FLOAT_STRING)); assertEquals(C.INT_STRING, String.valueOf(C.INT_VALUE)); @@ -293,6 +442,8 @@ /** * Helper method for testTrim to avoid compiler optimizations. + * + * TODO: insufficient, compiler now inlines. */ public void trimRightAssertEquals(String left, String right) { assertEquals(left, right.trim()); @@ -300,6 +451,8 @@ /** * Helper method for testTrim to avoid compiler optimizations. + * + * TODO: insufficient, compiler now inlines. */ public void trimRightAssertSame(String left, String right) { assertSame(left, right.trim());