Merge "Fix SimpleDateFormatTest when run outside California"
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 698ebc5..01991b0 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1374,6 +1374,11 @@
]
},
{
+ description: "ScannerParseLargeFileBenchmark can cause a failure due to a timeout",
+ bug: 14865710,
+ name: "org.apache.harmony.tests.java.util.ScannerParseLargeFileBenchmarkTest"
+},
+{
description: "Known failure in GregorianCalendarTest",
bug: 12778197,
name: "org.apache.harmony.tests.java.util.GregorianCalendarTest#test_computeTime"
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
index c1d592a..be40d0b 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
@@ -657,20 +657,35 @@
* @tests ServerSocketChannel#socket().getSoTimeout()
*/
public void test_accept_SOTIMEOUT() throws IOException {
- // regression test for Harmony-707
- final int SO_TIMEOUT = 10;
+ // Regression test for Harmony-707
+ // The timeout actually used may be different from the one set due to
+ // rounding by the Linux Kernel (see sock_set_timeout() in net/core/sock.c).
+ // getSoTimeout() can return a different value from the one set with
+ // setSoTimeout(). Consequently we do not check for equality with what was
+ // set.
+
ServerSocketChannel sc = ServerSocketChannel.open();
try {
sc.socket().bind(null);
+
+ // Non blocking mode, accept() will return NULL since there are no pending connections.
sc.configureBlocking(false);
+
ServerSocket ss = sc.socket();
+
+ int defaultTimeout = ss.getSoTimeout();
+ assertEquals(0, defaultTimeout);
+ // The timeout value is unimportant, providing it is large enough to be accepted
+ // by the Kernel as distinct from the default.
+ final int SO_TIMEOUT = 200;
ss.setSoTimeout(SO_TIMEOUT);
+ int nonDefaultTimeout = ss.getSoTimeout();
+ assertTrue(nonDefaultTimeout != defaultTimeout);
+
SocketChannel client = sc.accept();
- // non blocking mode, returns null since there are no pending connections.
assertNull(client);
- int soTimeout = ss.getSoTimeout();
- // Harmony fails here.
- assertEquals(SO_TIMEOUT, soTimeout);
+ // Confirm the timeout was unchanged.
+ assertEquals(nonDefaultTimeout, ss.getSoTimeout());
} finally {
sc.close();
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
index 70e41a2..1a6a25e 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
@@ -16,8 +16,6 @@
*/
package org.apache.harmony.tests.java.text;
-import java.io.File;
-import java.net.URL;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
@@ -25,7 +23,6 @@
import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.Locale;
-import java.util.ServiceConfigurationError;
public class DateFormatSymbolsTest extends junit.framework.TestCase {
@@ -402,13 +399,6 @@
dfs = new DateFormatSymbols(new Locale("en", "us"));
}
- /**
- * Tears down the fixture, for example, close a network connection. This
- * method is called after a test is executed.
- */
- protected void tearDown() {
- }
-
// Test serialization mechanism of DateFormatSymbols
public void test_serialization() throws Exception {
DateFormatSymbols symbols = new DateFormatSymbols(Locale.FRANCE);
@@ -426,7 +416,6 @@
DateFormatSymbols symbolsD = (DateFormatSymbols) objectIStream
.readObject();
- // The associated currency will not persist
String[][] zoneStringsD = symbolsD.getZoneStrings();
assertNotNull(zoneStringsD);
assertEquals(symbols, symbolsD);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerParseLargeFileBenchmarkTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerParseLargeFileBenchmarkTest.java
index 4b0d1ea..c0f9e58 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerParseLargeFileBenchmarkTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerParseLargeFileBenchmarkTest.java
@@ -24,11 +24,11 @@
public class ScannerParseLargeFileBenchmarkTest extends TestCase {
/**
- * This test will check when parse a large file like more than 200M bytes if
- * the Scanner will exhaust all heap memory
+ * Check whether the Scanner will exhaust all heap memory when parsing a
+ * large file.
*/
public void testParseLargeFile() throws Exception {
- MyReader reader = new MyReader();
+ FakeLargeFile reader = new FakeLargeFile();
String delimiter = "\r?\n";
Scanner scanner = new Scanner(reader).useDelimiter(delimiter);
@@ -39,14 +39,9 @@
reader.close();
}
- private static class MyReader extends Reader {
- static final char[] CONTENT = "large file!\n".toCharArray();
-
- static long fileLength = (8 << 21) * 12;
-
- static boolean first = true;
-
- static int position = 0;
+ private static class FakeLargeFile extends Reader {
+ private static final char[] CONTENT = "large file!\n".toCharArray();
+ private static final int FILE_LENGTH = 192 * 1024 * 1024; // 192 MB
private int count = 0;
@@ -55,22 +50,24 @@
}
@Override
- public int read(char[] buf, int offset, int length) {
- if (count >= fileLength) {
+ public int read(char[] buffer, int offset, int length) {
+ if (count >= FILE_LENGTH) {
return -1;
}
- if (first == true) {
- position = 0;
- first = false;
- }
- for (int i = offset; i < length; i++) {
- buf[i] = CONTENT[(i + position) % CONTENT.length];
- count++;
- }
- position = (length + position) % CONTENT.length;
-
- return length - offset;
+ final int charsToRead = Math.min(FILE_LENGTH - count, length);
+ int bufferIndex = offset;
+ int contentIndex = count % CONTENT.length;
+ int charsRead = 0;
+ while (charsRead < charsToRead) {
+ buffer[bufferIndex++] = CONTENT[contentIndex++];
+ if (contentIndex == CONTENT.length) {
+ contentIndex = 0;
+ }
+ charsRead++;
+ }
+ count += charsRead;
+ return charsToRead;
}
}
}
diff --git a/luni/src/main/java/java/lang/Character.java b/luni/src/main/java/java/lang/Character.java
index 8efd6cd..e920a3e 100644
--- a/luni/src/main/java/java/lang/Character.java
+++ b/luni/src/main/java/java/lang/Character.java
@@ -2530,25 +2530,28 @@
}
/**
- * Gets the Unicode directionality of the specified character.
- *
- * @param codePoint
- * the Unicode code point to get the directionality of.
- * @return the Unicode directionality of {@code codePoint}.
+ * Returns the Unicode directionality of the given code point.
+ * This will be one of the {@code DIRECTIONALITY_} constants.
+ * For characters whose directionality is undefined, or whose
+ * directionality has no appropriate constant in this class,
+ * {@code DIRECTIONALITY_UNDEFINED} is returned.
*/
public static byte getDirectionality(int codePoint) {
if (getType(codePoint) == Character.UNASSIGNED) {
return Character.DIRECTIONALITY_UNDEFINED;
}
- byte directionality = getDirectionalityImpl(codePoint);
- if (directionality == -1) {
- return -1;
+ byte directionality = getIcuDirectionality(codePoint);
+ if (directionality >= 0 && directionality < DIRECTIONALITY.length) {
+ return DIRECTIONALITY[directionality];
}
- return DIRECTIONALITY[directionality];
+ return Character.DIRECTIONALITY_UNDEFINED;
}
- private static native byte getDirectionalityImpl(int codePoint);
+ /**
+ * @hide - internal use only.
+ */
+ public static native byte getIcuDirectionality(int codePoint);
/**
* Indicates whether the specified character is mirrored.
diff --git a/luni/src/main/java/java/text/DateFormatSymbols.java b/luni/src/main/java/java/text/DateFormatSymbols.java
index 54a1a3f..ee34bbd 100644
--- a/luni/src/main/java/java/text/DateFormatSymbols.java
+++ b/luni/src/main/java/java/text/DateFormatSymbols.java
@@ -62,16 +62,14 @@
transient LocaleData localeData;
// Localized display names.
- String[][] zoneStrings;
- // Has the user called setZoneStrings?
- transient boolean customZoneStrings;
+ private String[][] zoneStrings;
- /**
- * Locale, necessary to lazily load time zone strings. We force the time
- * zone names to load upon serialization, so this will never be needed
- * post deserialization.
+ /*
+ * Locale, necessary to lazily load time zone strings. Added to the serialized form for
+ * Android's L release. May be null if the object was deserialized using data from older
+ * releases. See b/16502916.
*/
- transient final Locale locale;
+ private final Locale locale;
/**
* Gets zone strings, initializing them if necessary. Does not create
@@ -153,11 +151,12 @@
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
- // NOTE: We don't serialize the locale we were created with, so we can't
- // get back the localeData object we want. This is broken for callers that
- // access this field directly (i.e, SimpleDateFormat). We should ideally
- // have serialized the locale we were created with. See b/16502916.
- this.localeData = LocaleData.get(Locale.getDefault());
+ Locale locale = this.locale;
+ if (locale == null) {
+ // Before the L release Android did not serialize the locale. Handle its absence.
+ locale = Locale.getDefault();
+ }
+ this.localeData = LocaleData.get(locale);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
@@ -220,7 +219,6 @@
// 'zoneStrings' is so large, we just print a representative value.
return getClass().getName() +
"[amPmStrings=" + Arrays.toString(ampms) +
- ",customZoneStrings=" + customZoneStrings +
",eras=" + Arrays.toString(eras) +
",localPatternChars=" + localPatternChars +
",months=" + Arrays.toString(months) +
@@ -488,6 +486,25 @@
}
}
this.zoneStrings = clone2dStringArray(zoneStrings);
- this.customZoneStrings = true;
+ }
+
+ /**
+ * Returns the display name of the timezone specified. Returns null if no name was found in the
+ * zone strings.
+ *
+ * @param daylight whether to return the daylight savings or the standard name
+ * @param style one of the {@link TimeZone} styles such as {@link TimeZone#SHORT}
+ *
+ * @hide used internally
+ */
+ String getTimeZoneDisplayName(TimeZone tz, boolean daylight, int style) {
+ if (style != TimeZone.SHORT && style != TimeZone.LONG) {
+ throw new IllegalArgumentException("Bad style: " + style);
+ }
+
+ // If custom zone strings have been set with setZoneStrings() we use those. Otherwise we
+ // use the ones from LocaleData.
+ String[][] zoneStrings = internalZoneStrings();
+ return TimeZoneNames.getDisplayName(zoneStrings, tz.getID(), daylight, style);
}
}
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index 259bfe0..c1a8ee9 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -740,15 +740,9 @@
TimeZone tz = calendar.getTimeZone();
boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0);
int style = count < 4 ? TimeZone.SHORT : TimeZone.LONG;
- if (!formatData.customZoneStrings) {
- buffer.append(tz.getDisplayName(daylight, style, formatData.locale));
- return;
- }
- // We can't call TimeZone.getDisplayName() because it would not use
- // the custom DateFormatSymbols of this SimpleDateFormat.
- String custom = TimeZoneNames.getDisplayName(formatData.zoneStrings, tz.getID(), daylight, style);
- if (custom != null) {
- buffer.append(custom);
+ String zoneString = formatData.getTimeZoneDisplayName(tz, daylight, style);
+ if (zoneString != null) {
+ buffer.append(zoneString);
return;
}
}
@@ -759,21 +753,11 @@
// See http://www.unicode.org/reports/tr35/#Date_Format_Patterns for the different counts.
// @param generalTimeZone "GMT-08:00" rather than "-0800".
private void appendNumericTimeZone(StringBuffer buffer, int count, boolean generalTimeZone) {
- int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
- char sign = '+';
- if (offset < 0) {
- sign = '-';
- offset = -offset;
- }
- if (generalTimeZone || count == 4) {
- buffer.append("GMT");
- }
- buffer.append(sign);
- appendNumber(buffer, 2, offset / 3600000);
- if (generalTimeZone || count >= 4) {
- buffer.append(':');
- }
- appendNumber(buffer, 2, (offset % 3600000) / 60000);
+ int offsetMillis = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
+ boolean includeGmt = generalTimeZone || count == 4;
+ boolean includeMinuteSeparator = generalTimeZone || count >= 4;
+ buffer.append(TimeZone.createGmtOffsetString(includeGmt, includeMinuteSeparator,
+ offsetMillis));
}
private void appendMilliseconds(StringBuffer buffer, int count, int value) {
diff --git a/luni/src/main/java/java/util/Scanner.java b/luni/src/main/java/java/util/Scanner.java
index 7d504b7..7d0e795 100644
--- a/luni/src/main/java/java/util/Scanner.java
+++ b/luni/src/main/java/java/util/Scanner.java
@@ -159,12 +159,15 @@
if (charsetName == null) {
throw new IllegalArgumentException("charsetName == null");
}
+
+ InputStreamReader streamReader;
try {
- setInput(new InputStreamReader(fis, charsetName));
+ streamReader = new InputStreamReader(fis, charsetName);
} catch (UnsupportedEncodingException e) {
IoUtils.closeQuietly(fis);
throw new IllegalArgumentException(e.getMessage());
}
+ initialize(streamReader);
}
/**
@@ -174,7 +177,7 @@
* the string to be scanned.
*/
public Scanner(String src) {
- setInput(new StringReader(src));
+ initialize(new StringReader(src));
}
/**
@@ -203,11 +206,14 @@
if (src == null) {
throw new NullPointerException("src == null");
}
+
+ InputStreamReader streamReader;
try {
- setInput(new InputStreamReader(src, charsetName));
+ streamReader = new InputStreamReader(src, charsetName);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e.getMessage());
}
+ initialize(streamReader);
}
/**
@@ -220,7 +226,7 @@
if (src == null) {
throw new NullPointerException("src == null");
}
- setInput(src);
+ initialize(src);
}
/**
@@ -252,13 +258,14 @@
if (charsetName == null) {
throw new IllegalArgumentException("charsetName == null");
}
- setInput(Channels.newReader(src, charsetName));
+ initialize(Channels.newReader(src, charsetName));
}
- private void setInput(Readable input) {
+ private void initialize(Readable input) {
this.input = input;
- buffer.limit(0);
- matcher = delimiter.matcher(buffer);
+ matcher = delimiter.matcher("");
+ matcher.useTransparentBounds(true);
+ matcher.useAnchoringBounds(false);
}
/**
@@ -535,7 +542,7 @@
checkOpen();
checkNotNull(pattern);
matchSuccessful = false;
- saveCurrentStatus();
+ prepareForScan();
// if the next token exists, set the match region, otherwise return
// false
if (!setTokenRegion()) {
@@ -790,7 +797,7 @@
* @throws IllegalStateException if this {@code Scanner} is closed.
*/
public boolean hasNextLine() {
- saveCurrentStatus();
+ prepareForScan();
String result = findWithinHorizon(LINE_PATTERN, 0);
recoverPreviousStatus();
return result != null;
@@ -954,7 +961,7 @@
checkOpen();
checkNotNull(pattern);
matchSuccessful = false;
- saveCurrentStatus();
+ prepareForScan();
if (!setTokenRegion()) {
recoverPreviousStatus();
// if setting match region fails
@@ -1204,7 +1211,7 @@
Pattern floatPattern = getFloatPattern();
String floatString = next(floatPattern);
floatString = removeLocaleInfoFromFloat(floatString);
- double doubleValue = 0;
+ double doubleValue;
try {
doubleValue = Double.parseDouble(floatString);
} catch (NumberFormatException e) {
@@ -1248,7 +1255,7 @@
Pattern floatPattern = getFloatPattern();
String floatString = next(floatPattern);
floatString = removeLocaleInfoFromFloat(floatString);
- float floatValue = 0;
+ float floatValue;
try {
floatValue = Float.parseFloat(floatString);
} catch (NumberFormatException e) {
@@ -1310,7 +1317,7 @@
Pattern integerPattern = getIntegerPattern(radix);
String intString = next(integerPattern);
intString = removeLocaleInfo(intString, int.class);
- int intValue = 0;
+ int intValue;
try {
intValue = Integer.parseInt(intString, radix);
} catch (NumberFormatException e) {
@@ -1340,7 +1347,7 @@
matcher.usePattern(LINE_PATTERN);
matcher.region(findStartIndex, bufferLength);
- String result = null;
+ String result;
while (true) {
if (matcher.find()) {
if (inputExhausted || matcher.end() != bufferLength
@@ -1422,7 +1429,7 @@
Pattern integerPattern = getIntegerPattern(radix);
String intString = next(integerPattern);
intString = removeLocaleInfo(intString, int.class);
- long longValue = 0;
+ long longValue;
try {
longValue = Long.parseLong(intString, radix);
} catch (NumberFormatException e) {
@@ -1484,7 +1491,7 @@
Pattern integerPattern = getIntegerPattern(radix);
String intString = next(integerPattern);
intString = removeLocaleInfo(intString, int.class);
- short shortValue = 0;
+ short shortValue;
try {
shortValue = Short.parseShort(intString, radix);
} catch (NumberFormatException e) {
@@ -1662,23 +1669,46 @@
}
/*
- * Change the matcher's string after reading input
+ * Change the matcher's input after modifying the contents of the buffer.
+ * The current implementation of Matcher causes a copy of the buffer to be taken.
*/
private void resetMatcher() {
- if (matcher == null) {
- matcher = delimiter.matcher(buffer);
- } else {
- matcher.reset(buffer);
- }
- matcher.useTransparentBounds(true);
- matcher.useAnchoringBounds(false);
+ matcher.reset(buffer);
matcher.region(findStartIndex, bufferLength);
}
/*
- * Save the matcher's last find position
+ * Recover buffer space for characters that are already processed and save the matcher's state
+ * in case parsing fails. See recoverPrevousState. This method must be called before
+ * any buffer offsets are calculated.
*/
- private void saveCurrentStatus() {
+ private void prepareForScan() {
+ // Compacting the buffer recovers space taken by already processed characters. This does not
+ // prevent the buffer growing in all situations but keeps the buffer small when delimiters
+ // exist regularly.
+ if (findStartIndex >= buffer.capacity() / 2) {
+ // When over half the buffer is filled with characters no longer being considered by the
+ // scanner we take the cost of compacting the buffer.
+
+ // Move all characters from [findStartIndex, findStartIndex + remaining()) to
+ // [0, remaining()).
+ int oldPosition = buffer.position();
+ buffer.position(findStartIndex);
+ buffer.compact();
+ buffer.position(oldPosition);
+
+ // Update Scanner state to reflect the new buffer state.
+ bufferLength -= findStartIndex;
+ findStartIndex = 0;
+ preStartIndex = -1;
+
+ // The matcher must also be informed that the buffer has changed because it operates on
+ // a String copy.
+ resetMatcher();
+ }
+
+ // Save the matcher's last find position so it can be returned to if the next token cannot
+ // be parsed.
preStartIndex = findStartIndex;
}
@@ -1822,7 +1852,7 @@
boolean negative = removeLocaleSign(tokenBuilder);
// Remove group separator
String groupSeparator = String.valueOf(dfs.getGroupingSeparator());
- int separatorIndex = -1;
+ int separatorIndex;
while ((separatorIndex = tokenBuilder.indexOf(groupSeparator)) != -1) {
tokenBuilder.delete(separatorIndex, separatorIndex + 1);
}
@@ -1909,9 +1939,9 @@
*/
private boolean setTokenRegion() {
// The position where token begins
- int tokenStartIndex = 0;
+ int tokenStartIndex;
// The position where token ends
- int tokenEndIndex = 0;
+ int tokenEndIndex;
// Use delimiter pattern
matcher.usePattern(delimiter);
matcher.region(findStartIndex, bufferLength);
@@ -1945,8 +1975,7 @@
if (matcher.find()) {
findComplete = true;
// If just delimiter remains
- if (matcher.start() == findStartIndex
- && matcher.end() == bufferLength) {
+ if (matcher.start() == findStartIndex && matcher.end() == bufferLength) {
// If more input resource exists
if (!inputExhausted) {
readMore();
@@ -1964,7 +1993,7 @@
}
}
tokenStartIndex = matcher.end();
- findStartIndex = matcher.end();
+ findStartIndex = tokenStartIndex;
return tokenStartIndex;
}
@@ -1984,7 +2013,7 @@
setSuccess = true;
}
// If the first delimiter of scanner is not at the find start position
- if (-1 != findIndex && preStartIndex != matcher.start()) {
+ if (findIndex != -1 && preStartIndex != matcher.start()) {
tokenStartIndex = preStartIndex;
tokenEndIndex = matcher.start();
findStartIndex = matcher.start();
@@ -1996,7 +2025,7 @@
}
private int findDelimiterAfter() {
- int tokenEndIndex = 0;
+ int tokenEndIndex;
boolean findComplete = false;
while (!findComplete) {
if (matcher.find()) {
@@ -2014,7 +2043,7 @@
}
}
tokenEndIndex = matcher.start();
- findStartIndex = matcher.start();
+ findStartIndex = tokenEndIndex;
return tokenEndIndex;
}
@@ -2032,7 +2061,7 @@
}
// Read input resource
- int readCount = 0;
+ int readCount;
try {
buffer.limit(buffer.capacity());
buffer.position(oldBufferLength);
diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java
index e4c68c5..854a4a6 100644
--- a/luni/src/main/java/java/util/TimeZone.java
+++ b/luni/src/main/java/java/util/TimeZone.java
@@ -63,7 +63,7 @@
*
* @see Calendar
* @see GregorianCalendar
- * @see SimpleDateFormat
+ * @see java.text.SimpleDateFormat
*/
public abstract class TimeZone implements Serializable, Cloneable {
private static final long serialVersionUID = 3581463369166924961L;
@@ -206,27 +206,48 @@
// upgrade to icu4c 50 and rewrite the underlying native code. See also the
// "element[j] != null" check in SimpleDateFormat.parseTimeZone, and the extra work in
// DateFormatSymbols.getZoneStrings.
-
- int offset = getRawOffset();
+ int offsetMillis = getRawOffset();
if (daylightTime) {
- offset += getDSTSavings();
+ offsetMillis += getDSTSavings();
}
- offset /= 60000;
+ return createGmtOffsetString(true /* includeGmt */, true /* includeMinuteSeparator */,
+ offsetMillis);
+ }
+
+ /**
+ * Returns a string representation of an offset from UTC.
+ *
+ * <p>The format is "[GMT](+|-)HH[:]MM". The output is not localized.
+ *
+ * @param includeGmt true to include "GMT", false to exclude
+ * @param includeMinuteSeparator true to include the separator between hours and minutes, false
+ * to exclude.
+ * @param offsetMillis the offset from UTC
+ *
+ * @hide used internally by SimpleDateFormat
+ */
+ public static String createGmtOffsetString(boolean includeGmt,
+ boolean includeMinuteSeparator, int offsetMillis) {
+ int offsetMinutes = offsetMillis / 60000;
char sign = '+';
- if (offset < 0) {
+ if (offsetMinutes < 0) {
sign = '-';
- offset = -offset;
+ offsetMinutes = -offsetMinutes;
}
StringBuilder builder = new StringBuilder(9);
- builder.append("GMT");
+ if (includeGmt) {
+ builder.append("GMT");
+ }
builder.append(sign);
- appendNumber(builder, 2, offset / 60);
- builder.append(':');
- appendNumber(builder, 2, offset % 60);
+ appendNumber(builder, 2, offsetMinutes / 60);
+ if (includeMinuteSeparator) {
+ builder.append(':');
+ }
+ appendNumber(builder, 2, offsetMinutes % 60);
return builder.toString();
}
- private void appendNumber(StringBuilder builder, int count, int value) {
+ private static void appendNumber(StringBuilder builder, int count, int value) {
String string = Integer.toString(value);
for (int i = 0; i < count - string.length(); i++) {
builder.append('0');
diff --git a/luni/src/main/java/libcore/util/EmptyArray.java b/luni/src/main/java/libcore/util/EmptyArray.java
index eb91589c..eac06f2 100644
--- a/luni/src/main/java/libcore/util/EmptyArray.java
+++ b/luni/src/main/java/libcore/util/EmptyArray.java
@@ -23,6 +23,7 @@
public static final byte[] BYTE = new byte[0];
public static final char[] CHAR = new char[0];
public static final double[] DOUBLE = new double[0];
+ public static final float[] FLOAT = new float[0];
public static final int[] INT = new int[0];
public static final long[] LONG = new long[0];
diff --git a/luni/src/main/native/java_lang_Character.cpp b/luni/src/main/native/java_lang_Character.cpp
index 2d1fcfc..4022f4b 100644
--- a/luni/src/main/native/java_lang_Character.cpp
+++ b/luni/src/main/native/java_lang_Character.cpp
@@ -33,7 +33,7 @@
return u_charType(codePoint);
}
-static jbyte Character_getDirectionalityImpl(JNIEnv*, jclass, jint codePoint) {
+static jbyte Character_getIcuDirectionality(JNIEnv*, jclass, jint codePoint) {
return u_charDirection(codePoint);
}
@@ -166,7 +166,7 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Character, digitImpl, "!(II)I"),
- NATIVE_METHOD(Character, getDirectionalityImpl, "!(I)B"),
+ NATIVE_METHOD(Character, getIcuDirectionality, "!(I)B"),
NATIVE_METHOD(Character, getNameImpl, "(I)Ljava/lang/String;"),
NATIVE_METHOD(Character, getNumericValueImpl, "!(I)I"),
NATIVE_METHOD(Character, getTypeImpl, "!(I)I"),
diff --git a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
index faf87f1..a7c9098 100644
--- a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
+++ b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
@@ -75,7 +75,6 @@
const UDate now(Calendar::getNow());
static const UnicodeString kUtc("UTC", 3, US_INV);
- static const UnicodeString pacific_apia("Pacific/Apia", 12, US_INV);
size_t id_count = env->GetArrayLength(result);
for (size_t i = 0; i < id_count; ++i) {
@@ -104,11 +103,6 @@
// every language).
// TODO: check CLDR doesn't actually have this somewhere.
long_std = short_std = long_dst = short_dst = kUtc;
- } else if (zone_id.unicodeString() == pacific_apia) {
- // icu4c 50 doesn't know Samoa has DST yet. http://b/7955614
- if (long_dst.isBogus()) {
- long_dst = "Samoa Daylight Time";
- }
}
bool okay =
diff --git a/luni/src/test/java/libcore/java/lang/CharacterTest.java b/luni/src/test/java/libcore/java/lang/CharacterTest.java
index 94e3b96..8c6f06f 100644
--- a/luni/src/test/java/libcore/java/lang/CharacterTest.java
+++ b/luni/src/test/java/libcore/java/lang/CharacterTest.java
@@ -277,4 +277,16 @@
}
}
}
+
+ // http://b/15492712
+ public void test_getDirectionality() throws Exception {
+ // We shouldn't throw an exception for any code point.
+ for (int c = '\u0000'; c <= Character.MAX_VALUE; ++c) {
+ Character.getDirectionality(c);
+ }
+ assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character.getDirectionality(0x2066));
+ assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character.getDirectionality(0x2067));
+ assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character.getDirectionality(0x2068));
+ assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character.getDirectionality(0x2069));
+ }
}
diff --git a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
index 9ab6f70..057cd17 100644
--- a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
+++ b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
@@ -46,8 +46,8 @@
}
public void testSerialization() throws Exception {
- // Set the default locale. The default locale determines what strings are used by the
- // DateFormatSymbols after deserialization.
+ // Set the default locale. The default locale used to determine what strings were used by
+ // the DateFormatSymbols after deserialization. See http://b/16502916
Locale.setDefault(Locale.US);
// The Polish language needs stand-alone month and weekday names.
@@ -64,17 +64,16 @@
DateFormatSymbols deserializedDfs = (DateFormatSymbols) in.readObject();
assertEquals(-1, in.read());
- // The two objects should claim to be equal, even though they aren't really.
+ // The two objects be equal.
assertEquals(originalDfs, deserializedDfs);
// The original differentiates between regular month names and stand-alone month names...
assertEquals("stycznia", formatDate(pl, "MMMM", originalDfs));
assertEquals("stycze\u0144", formatDate(pl, "LLLL", originalDfs));
- // But the deserialized object is screwed because the RI's serialized form doesn't
- // contain the locale or the necessary strings. Don't serialize DateFormatSymbols, folks!
+ // And so does the deserialized version.
assertEquals("stycznia", formatDate(pl, "MMMM", deserializedDfs));
- assertEquals("January", formatDate(pl, "LLLL", deserializedDfs));
+ assertEquals("stycze\u0144", formatDate(pl, "LLLL", deserializedDfs));
}
private String formatDate(Locale l, String fmt, DateFormatSymbols dfs) {