Merge changes from topic "vh-fences-coverage"
* changes:
Coverage: mark VarHandles as exempt from jacoco
Add a coverage test for VarHandle fences
diff --git a/EXPECTED_UPSTREAM b/EXPECTED_UPSTREAM
index 982b3e0..fc0bf38 100644
--- a/EXPECTED_UPSTREAM
+++ b/EXPECTED_UPSTREAM
@@ -728,24 +728,24 @@
ojluni/src/main/java/java/text/CollationElementIterator.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/CollationElementIterator.java
ojluni/src/main/java/java/text/CollationKey.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/CollationKey.java
ojluni/src/main/java/java/text/Collator.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/Collator.java
-ojluni/src/main/java/java/text/DateFormat.java,jdk8u/jdk8u121-b13,jdk/src/share/classes/java/text/DateFormat.java
-ojluni/src/main/java/java/text/DateFormatSymbols.java,jdk8u/jdk8u121-b13,jdk/src/share/classes/java/text/DateFormatSymbols.java
-ojluni/src/main/java/java/text/DecimalFormat.java,jdk8u/jdk8u121-b13,jdk/src/share/classes/java/text/DecimalFormat.java
-ojluni/src/main/java/java/text/DecimalFormatSymbols.java,jdk8u/jdk8u121-b13,jdk/src/share/classes/java/text/DecimalFormatSymbols.java
+ojluni/src/main/java/java/text/DateFormat.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/DateFormat.java
+ojluni/src/main/java/java/text/DateFormatSymbols.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/DateFormatSymbols.java
+ojluni/src/main/java/java/text/DecimalFormat.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/DecimalFormat.java
+ojluni/src/main/java/java/text/DecimalFormatSymbols.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/DecimalFormatSymbols.java
ojluni/src/main/java/java/text/DontCareFieldPosition.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/DontCareFieldPosition.java
ojluni/src/main/java/java/text/EntryPair.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/EntryPair.java
ojluni/src/main/java/java/text/FieldPosition.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/FieldPosition.java
ojluni/src/main/java/java/text/Format.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/Format.java
# java.text.IcuIteratorWrapper doesn't come from the upstream OpenJDK.
ojluni/src/main/java/java/text/MergeCollation.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/MergeCollation.java
-ojluni/src/main/java/java/text/MessageFormat.java,jdk8u/jdk8u121-b13,jdk/src/share/classes/java/text/MessageFormat.java
+ojluni/src/main/java/java/text/MessageFormat.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/MessageFormat.java
ojluni/src/main/java/java/text/Normalizer.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/Normalizer.java
-ojluni/src/main/java/java/text/NumberFormat.java,jdk8u/jdk8u121-b13,jdk/src/share/classes/java/text/NumberFormat.java
+ojluni/src/main/java/java/text/NumberFormat.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/NumberFormat.java
ojluni/src/main/java/java/text/ParseException.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/ParseException.java
ojluni/src/main/java/java/text/ParsePosition.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/ParsePosition.java
ojluni/src/main/java/java/text/PatternEntry.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/PatternEntry.java
ojluni/src/main/java/java/text/RuleBasedCollator.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/RuleBasedCollator.java
-ojluni/src/main/java/java/text/SimpleDateFormat.java,jdk8u/jdk8u121-b13,jdk/src/share/classes/java/text/SimpleDateFormat.java
+ojluni/src/main/java/java/text/SimpleDateFormat.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/SimpleDateFormat.java
ojluni/src/main/java/java/text/StringCharacterIterator.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/text/StringCharacterIterator.java
ojluni/src/main/java/java/time/Clock.java,jdk8u/jdk8u121-b13,jdk/src/share/classes/java/time/Clock.java
ojluni/src/main/java/java/time/DateTimeException.java,jdk11u/jdk-11.0.13-ga,src/java.base/share/classes/java/time/DateTimeException.java
@@ -1867,4 +1867,7 @@
ojluni/src/test/java/nio/channels/Selector/ByteServer.java,jdk11u/jdk-11.0.13-ga,test/jdk/java/nio/channels/Selector/ByteServer.java
ojluni/src/test/java/nio/channels/Selector/SelectAfterRead.java,jdk11u/jdk-11.0.13-ga,test/jdk/java/nio/channels/Selector/SelectAfterRead.java
ojluni/src/test/java/nio/channels/Selector/SelectWithConsumer.java,jdk11u/jdk-11+28,test/jdk/java/nio/channels/Selector/SelectWithConsumer.java
+ojluni/src/test/java/security/cert/CertPathEncodingTest.java,jdk11u/jdk-11.0.13-ga,test/jdk/java/security/cert/CertPathEncodingTest.java
+ojluni/src/test/java/security/cert/GetInstance.java,jdk11u/jdk-11.0.13-ga,test/jdk/java/security/cert/GetInstance.java
ojluni/src/test/java/security/cert/URICertStoreParameters/TestBasic.java,jdk11u/jdk-11.0.13-ga,test/jdk/java/security/cert/URICertStoreParameters/TestBasic.java
+ojluni/src/test/java/security/cert/X509CertSelectorTest.java,jdk11u/jdk-11.0.13-ga,test/jdk/java/security/cert/X509CertSelectorTest.java
diff --git a/JavaLibrary.bp b/JavaLibrary.bp
index 43fe7ba..8490ded 100644
--- a/JavaLibrary.bp
+++ b/JavaLibrary.bp
@@ -552,6 +552,11 @@
libs: [
"junit",
],
+ errorprone: {
+ javacflags: [
+ "-Xep:DoNotCall:OFF",
+ ],
+ },
}
// A filegroup that provides access to a source file for a toolchain test that
diff --git a/README.md b/README.md
index 9fbbf08..6044f3f 100644
--- a/README.md
+++ b/README.md
@@ -9,3 +9,5 @@
[Import files from OpenJDK](tools/expected_upstream/README.md)
[Add nullability annotations](ojluni/annotations/README)
+
+[Run benchmark tests](benchmarks/README.md)
diff --git a/benchmarks/AndroidManifest.xml b/benchmarks/AndroidManifest.xml
index 8674b09..611b18b 100644
--- a/benchmarks/AndroidManifest.xml
+++ b/benchmarks/AndroidManifest.xml
@@ -17,6 +17,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="libcore.benchmark">
+ <!-- Important: disable debuggable for accurate performance results -->
+ <application android:debuggable="false"/>
+
<instrumentation
android:name="androidx.benchmark.junit4.AndroidBenchmarkRunner"
android:targetPackage="libcore.benchmark"/>
diff --git a/benchmarks/README.md b/benchmarks/README.md
new file mode 100644
index 0000000..7fef94d
--- /dev/null
+++ b/benchmarks/README.md
@@ -0,0 +1,57 @@
+
+# Run Caliper benchmark tests using vogar on a rooted device
+
+- It uses the [Caliper library](https://github.com/google/caliper) developed by Google.
+- Vogar source codes can be found at `external/vogar`.
+
+1. Preparation
+
+```shell
+# vogar requires com.android.art.testing
+m vogar com.android.art.testing
+# remount if you haven't done so.
+adb root && adb remount && adb reboot && adb wait-for-device root
+cd libcore/benchmarks/src
+```
+
+Extra options to reduce noise:
+```shell
+adb shell stop # to kill frameworks and zygote
+```
+
+2. Run an individual test
+
+```shell
+vogar --benchmark benchmarks/regression/ScannerBenchmark.java
+```
+
+The source code of the tests can be found at `src/benchmarks/`
+
+# Run Jetpack benchmark tests
+Docs about Jetpack Benchmark can be found at
+[https://developer.android.com/studio/profile/benchmarking-overview]()
+
+1. Preparation
+
+To lock CPU clocks on a rooted device,
+run the script provided at [https://developer.android.com/studio/profile/run-benchmarks-in-ci#clock-locking]().
+
+2. Run an individual test
+```shell
+atest LibcoreBenchmarkTests:libcore.benchmark.FormatterTest#stringFormatNumber_allLocales
+```
+
+The source code of the tests can be found at `src_androidx/libcore/benchmark/`
+
+## Outdated documentation / Not working
+
+###VM Options
+
+
+The VM's configuration will have a substantial impact on performance.
+Use Caliper's -J<name> <value 1>,<value 2>,<value 3> syntax to compare different VM options. For example:
+```shell
+vogar --benchmark ~/svn/dalvik/benchmarks/regression/CrespoFileIoRegressionBenchmark.java \
+-- -Jgc -Xgc:noconcurrent,-Xgc:concurrent -Jint -Xint:fast,-Xint:jit,-Xint:portable
+```
+
diff --git a/luni/src/test/java/libcore/java/text/DateFormatTest.java b/luni/src/test/java/libcore/java/text/DateFormatTest.java
index 8e215ab..83505e5 100644
--- a/luni/src/test/java/libcore/java/text/DateFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/DateFormatTest.java
@@ -16,6 +16,7 @@
package libcore.java.text;
+import java.util.TimeZone;
import junit.framework.TestCase;
import java.text.DateFormat;
@@ -56,4 +57,12 @@
String expectedDateString = sdf.format(current);
assertEquals(expectedDateString, actualDateString);
}
+
+ public void testGetTimeInstance_withLocaleExtension() {
+ Locale locale = Locale.forLanguageTag("en-u-tz-usden");
+ DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT, locale);
+ // Note: when Calendar class supports "tz" extension, it should return "America/Denver".
+ assertEquals(TimeZone.getDefault().getID(), df.getCalendar().getTimeZone().getID());
+ // assertEquals("America/Denver", df.getCalendar().getTimeZone().getID());
+ }
}
diff --git a/luni/src/test/java/libcore/java/text/NumberFormatTest.java b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
index 547653f..599630b 100644
--- a/luni/src/test/java/libcore/java/text/NumberFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
@@ -260,4 +260,14 @@
format.setCurrency(Currency.getInstance("USD"));
assertEquals("$10", format.format(10d));
}
+
+ public void test_getInstance_withLocaleExtension() {
+ Locale locale = Locale.US;
+ NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
+ assertEquals("123", numberFormat.format(123));
+
+ locale = Locale.forLanguageTag("en-US-u-nu-arab");
+ numberFormat = NumberFormat.getNumberInstance(locale);
+ assertEquals("١٢٣", numberFormat.format(123));
+ }
}
diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
index 7db90cd..452b687 100644
--- a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
+++ b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
@@ -106,4 +106,13 @@
assertEquals(23, accessor.getLong(ChronoField.HOUR_OF_DAY));
assertEquals(59, accessor.getLong(ChronoField.MINUTE_OF_HOUR));
}
+
+ // Regression test for http://b/206566562 when ICU4J fails to load the pattern data.
+ @Test
+ public void test_format_locale_de_AT() {
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
+ .withLocale(new Locale("de", "AT"))
+ .withZone(ZoneOffset.UTC);
+ assertEquals("00:00", dateTimeFormatter.format(TEST_INSTANT));
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java b/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
index 29ceaebe..4a62623 100644
--- a/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
+++ b/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
@@ -434,4 +434,30 @@
} catch (NullPointerException expected) {
}
}
+
+ public void test_get_returnType() {
+ List<TestBase> input = new ArrayList();
+ input.add(new TestBase());
+ input.add(new TestDerived());
+ List<TestBase> l = new CopyOnWriteArrayList(input);
+
+ assertEquals(TestBase.class, l.get(0).getClass());
+ assertEquals(TestDerived.class, l.get(1).getClass());
+ }
+
+ public void test_toArray_returnType() {
+ List<TestBase> input = new ArrayList();
+ input.add(new TestBase());
+ input.add(new TestDerived());
+ List<TestBase> l = new CopyOnWriteArrayList(input);
+
+ assertEquals(Object[].class, l.toArray().getClass());
+ }
+
+
+ private static class TestBase {
+ }
+
+ private static class TestDerived extends TestBase {
+ }
}
diff --git a/ojluni/src/main/java/java/net/NetworkInterface.java b/ojluni/src/main/java/java/net/NetworkInterface.java
index bddee8a..d38d2ba 100644
--- a/ojluni/src/main/java/java/net/NetworkInterface.java
+++ b/ojluni/src/main/java/java/net/NetworkInterface.java
@@ -393,6 +393,7 @@
}
// Android-added: Document restrictions for non-system apps. http://b/170188668
+ // Android-added: Note about NullPointerException in older versions. http://b/206053582
/**
* Returns all the interfaces on this machine. The {@code Enumeration}
* contains at least one element, possibly representing a loopback
@@ -404,6 +405,10 @@
* <p>
* For non-system apps, this method will only return information for
* {@link NetworkInterface}s associated with an {@link InetAddress}.
+ * <p>
+ * ANDROID NOTE: On Android versions before S (API level 31), this method may throw a
+ * NullPointerException if called in an environment where there is a virtual
+ * interface without a parent interface present.
*
* @return an Enumeration of NetworkInterfaces found on this machine
* that <a href="#access-restrictions">are accessible</a>.
diff --git a/ojluni/src/main/java/java/text/DateFormat.java b/ojluni/src/main/java/java/text/DateFormat.java
index 8622c2f..7564363 100644
--- a/ojluni/src/main/java/java/text/DateFormat.java
+++ b/ojluni/src/main/java/java/text/DateFormat.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,10 @@
import java.util.TimeZone;
import libcore.icu.ICU;
+// Android-removed: Remove javadoc related to "rg" and "ca" Locale extension.
+// The "ca" extension isn't supported, because Android's java.text supports Gregorian calendar only.
+// The "rg" extension isn't supported until https://unicode-org.atlassian.net/browse/ICU-21831
+// is resolved, because java.text.* stack relies on ICU on resource resolution.
/**
* {@code DateFormat} is an abstract class for date/time formatting subclasses which
* formats and parses dates or time in a language-independent manner.
@@ -56,7 +60,6 @@
* formatting (i.e., date → text), parsing (text → date), and
* normalization. The date is represented as a <code>Date</code> object or
* as the milliseconds since January 1, 1970, 00:00:00 GMT.
- *
* <p>{@code DateFormat} provides many class methods for obtaining default date/time
* formatters based on the default or a given locale and a number of formatting
* styles. The formatting styles include {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, and {@link #SHORT}. More
@@ -93,6 +96,12 @@
* DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
* }</pre>
* </blockquote>
+ *
+ * Starting from Android 13, <p>if the specified locale contains "tz" (timezone)
+ * <a href="../util/Locale.html#def_locale_extension">Unicode
+ * extensions</a>, the time zone for formatting
+ * are overridden.
+ *
* <p>You can use a DateFormat to parse also.
* <blockquote>
* <pre>{@code
@@ -129,7 +138,7 @@
* on the screen.
* </ul>
*
- * <h3><a name="synchronization">Synchronization</a></h3>
+ * <h3><a id="synchronization">Synchronization</a></h3>
*
* <p>
* Date formats are not synchronized.
@@ -137,6 +146,25 @@
* If multiple threads access a format concurrently, it must be synchronized
* externally.
*
+ * @implSpec
+ * <ul><li>The {@link #format(Date, StringBuffer, FieldPosition)} and
+ * {@link #parse(String, ParsePosition)} methods may throw
+ * {@code NullPointerException}, if any of their parameter is {@code null}.
+ * The subclass may provide its own implementation and specification about
+ * {@code NullPointerException}.</li>
+ * <li>The {@link #setCalendar(Calendar)}, {@link
+ * #setNumberFormat(NumberFormat)} and {@link #setTimeZone(TimeZone)} methods
+ * do not throw {@code NullPointerException} when their parameter is
+ * {@code null}, but any subsequent operations on the same instance may throw
+ * {@code NullPointerException}.</li>
+ * <li>The {@link #getCalendar()}, {@link #getNumberFormat()} and
+ * {@link getTimeZone()} methods may return {@code null}, if the respective
+ * values of this instance is set to {@code null} through the corresponding
+ * setter methods. For Example: {@link #getTimeZone()} may return {@code null},
+ * if the {@code TimeZone} value of this instance is set as
+ * {@link #setTimeZone(java.util.TimeZone) setTimeZone(null)}.</li>
+ * </ul>
+ *
* @see Format
* @see NumberFormat
* @see SimpleDateFormat
@@ -144,6 +172,7 @@
* @see java.util.GregorianCalendar
* @see java.util.TimeZone
* @author Mark Davis, Chen-Lieh Huang, Alan Liu
+ * @since 1.1
*/
public abstract class DateFormat extends Format {
@@ -171,127 +200,128 @@
* Useful constant for ERA field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int ERA_FIELD = 0;
+ public static final int ERA_FIELD = 0;
/**
* Useful constant for YEAR field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int YEAR_FIELD = 1;
+ public static final int YEAR_FIELD = 1;
/**
* Useful constant for MONTH field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int MONTH_FIELD = 2;
+ public static final int MONTH_FIELD = 2;
/**
* Useful constant for DATE field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int DATE_FIELD = 3;
+ public static final int DATE_FIELD = 3;
/**
* Useful constant for one-based HOUR_OF_DAY field alignment.
* Used in FieldPosition of date/time formatting.
* HOUR_OF_DAY1_FIELD is used for the one-based 24-hour clock.
* For example, 23:59 + 01:00 results in 24:59.
*/
- public final static int HOUR_OF_DAY1_FIELD = 4;
+ public static final int HOUR_OF_DAY1_FIELD = 4;
/**
* Useful constant for zero-based HOUR_OF_DAY field alignment.
* Used in FieldPosition of date/time formatting.
* HOUR_OF_DAY0_FIELD is used for the zero-based 24-hour clock.
* For example, 23:59 + 01:00 results in 00:59.
*/
- public final static int HOUR_OF_DAY0_FIELD = 5;
+ public static final int HOUR_OF_DAY0_FIELD = 5;
/**
* Useful constant for MINUTE field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int MINUTE_FIELD = 6;
+ public static final int MINUTE_FIELD = 6;
/**
* Useful constant for SECOND field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int SECOND_FIELD = 7;
+ public static final int SECOND_FIELD = 7;
/**
* Useful constant for MILLISECOND field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int MILLISECOND_FIELD = 8;
+ public static final int MILLISECOND_FIELD = 8;
/**
* Useful constant for DAY_OF_WEEK field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int DAY_OF_WEEK_FIELD = 9;
+ public static final int DAY_OF_WEEK_FIELD = 9;
/**
* Useful constant for DAY_OF_YEAR field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int DAY_OF_YEAR_FIELD = 10;
+ public static final int DAY_OF_YEAR_FIELD = 10;
/**
* Useful constant for DAY_OF_WEEK_IN_MONTH field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
+ public static final int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
/**
* Useful constant for WEEK_OF_YEAR field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int WEEK_OF_YEAR_FIELD = 12;
+ public static final int WEEK_OF_YEAR_FIELD = 12;
/**
* Useful constant for WEEK_OF_MONTH field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int WEEK_OF_MONTH_FIELD = 13;
+ public static final int WEEK_OF_MONTH_FIELD = 13;
/**
* Useful constant for AM_PM field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int AM_PM_FIELD = 14;
+ public static final int AM_PM_FIELD = 14;
/**
* Useful constant for one-based HOUR field alignment.
* Used in FieldPosition of date/time formatting.
* HOUR1_FIELD is used for the one-based 12-hour clock.
* For example, 11:30 PM + 1 hour results in 12:30 AM.
*/
- public final static int HOUR1_FIELD = 15;
+ public static final int HOUR1_FIELD = 15;
/**
* Useful constant for zero-based HOUR field alignment.
* Used in FieldPosition of date/time formatting.
* HOUR0_FIELD is used for the zero-based 12-hour clock.
* For example, 11:30 PM + 1 hour results in 00:30 AM.
*/
- public final static int HOUR0_FIELD = 16;
+ public static final int HOUR0_FIELD = 16;
/**
* Useful constant for TIMEZONE field alignment.
* Used in FieldPosition of date/time formatting.
*/
- public final static int TIMEZONE_FIELD = 17;
+ public static final int TIMEZONE_FIELD = 17;
// Proclaim serial compatibility with 1.1 FCS
private static final long serialVersionUID = 7218322306649953788L;
/**
- * Overrides Format.
- * Formats a time object into a time string. Examples of time objects
- * are a time value expressed in milliseconds and a Date object.
- * @param obj must be a Number or a Date.
- * @param toAppendTo the string buffer for the returning time string.
- * @return the string buffer passed in as toAppendTo, with formatted text appended.
- * @param fieldPosition keeps track of the position of the field
- * within the returned string.
- * On input: an alignment field,
- * if desired. On output: the offsets of the alignment field. For
- * example, given a time text "1996.07.10 AD at 15:08:56 PDT",
- * if the given fieldPosition is DateFormat.YEAR_FIELD, the
- * begin index and end index of fieldPosition will be set to
- * 0 and 4, respectively.
- * Notice that if the same time field appears
- * more than once in a pattern, the fieldPosition will be set for the first
- * occurrence of that time field. For instance, formatting a Date to
- * the time string "1 PM PDT (Pacific Daylight Time)" using the pattern
- * "h a z (zzzz)" and the alignment field DateFormat.TIMEZONE_FIELD,
- * the begin index and end index of fieldPosition will be set to
- * 5 and 8, respectively, for the first occurrence of the timezone
- * pattern character 'z'.
+ * Formats the given {@code Object} into a date-time string. The formatted
+ * string is appended to the given {@code StringBuffer}.
+ *
+ * @param obj Must be a {@code Date} or a {@code Number} representing a
+ * millisecond offset from the <a href="../util/Calendar.html#Epoch">Epoch</a>.
+ * @param toAppendTo The string buffer for the returning date-time string.
+ * @param fieldPosition keeps track on the position of the field within
+ * the returned string. For example, given a date-time text
+ * {@code "1996.07.10 AD at 15:08:56 PDT"}, if the given {@code fieldPosition}
+ * is {@link DateFormat#YEAR_FIELD}, the begin index and end index of
+ * {@code fieldPosition} will be set to 0 and 4, respectively.
+ * Notice that if the same date-time field appears more than once in a
+ * pattern, the {@code fieldPosition} will be set for the first occurrence
+ * of that date-time field. For instance, formatting a {@code Date} to the
+ * date-time string {@code "1 PM PDT (Pacific Daylight Time)"} using the
+ * pattern {@code "h a z (zzzz)"} and the alignment field
+ * {@link DateFormat#TIMEZONE_FIELD}, the begin index and end index of
+ * {@code fieldPosition} will be set to 5 and 8, respectively, for the
+ * first occurrence of the timezone pattern character {@code 'z'}.
+ * @return the string buffer passed in as {@code toAppendTo},
+ * with formatted text appended.
+ * @exception IllegalArgumentException if the {@code Format} cannot format
+ * the given {@code obj}.
* @see java.text.Format
*/
public final StringBuffer format(Object obj, StringBuffer toAppendTo,
@@ -307,34 +337,35 @@
}
/**
- * Formats a Date into a date/time string.
- * @param date a Date to be formatted into a date/time string.
- * @param toAppendTo the string buffer for the returning date/time string.
- * @param fieldPosition keeps track of the position of the field
- * within the returned string.
- * On input: an alignment field,
- * if desired. On output: the offsets of the alignment field. For
- * example, given a time text "1996.07.10 AD at 15:08:56 PDT",
- * if the given fieldPosition is DateFormat.YEAR_FIELD, the
- * begin index and end index of fieldPosition will be set to
- * 0 and 4, respectively.
- * Notice that if the same time field appears
- * more than once in a pattern, the fieldPosition will be set for the first
- * occurrence of that time field. For instance, formatting a Date to
- * the time string "1 PM PDT (Pacific Daylight Time)" using the pattern
- * "h a z (zzzz)" and the alignment field DateFormat.TIMEZONE_FIELD,
- * the begin index and end index of fieldPosition will be set to
- * 5 and 8, respectively, for the first occurrence of the timezone
- * pattern character 'z'.
- * @return the string buffer passed in as toAppendTo, with formatted text appended.
+ * Formats a {@link Date} into a date-time string. The formatted
+ * string is appended to the given {@code StringBuffer}.
+ *
+ * @param date a Date to be formatted into a date-time string.
+ * @param toAppendTo the string buffer for the returning date-time string.
+ * @param fieldPosition keeps track on the position of the field within
+ * the returned string. For example, given a date-time text
+ * {@code "1996.07.10 AD at 15:08:56 PDT"}, if the given {@code fieldPosition}
+ * is {@link DateFormat#YEAR_FIELD}, the begin index and end index of
+ * {@code fieldPosition} will be set to 0 and 4, respectively.
+ * Notice that if the same date-time field appears more than once in a
+ * pattern, the {@code fieldPosition} will be set for the first occurrence
+ * of that date-time field. For instance, formatting a {@code Date} to the
+ * date-time string {@code "1 PM PDT (Pacific Daylight Time)"} using the
+ * pattern {@code "h a z (zzzz)"} and the alignment field
+ * {@link DateFormat#TIMEZONE_FIELD}, the begin index and end index of
+ * {@code fieldPosition} will be set to 5 and 8, respectively, for the
+ * first occurrence of the timezone pattern character {@code 'z'}.
+ * @return the string buffer passed in as {@code toAppendTo}, with formatted
+ * text appended.
*/
public abstract StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition fieldPosition);
/**
- * Formats a Date into a date/time string.
- * @param date the time value to be formatted into a time string.
- * @return the formatted time string.
+ * Formats a {@link Date} into a date-time string.
+ *
+ * @param date the time value to be formatted into a date-time string.
+ * @return the formatted date-time string.
*/
public final String format(Date date)
{
@@ -414,7 +445,7 @@
* index information as described above.
* @return A <code>Date</code> parsed from the string. In case of
* error, returns null.
- * @exception NullPointerException if <code>pos</code> is null.
+ * @throws NullPointerException if {@code source} or {@code pos} is null.
*/
public Object parseObject(String source, ParsePosition pos) {
return parse(source, pos);
@@ -451,7 +482,7 @@
* @see java.util.Locale.Category#FORMAT
* @return a time formatter.
*/
- public final static DateFormat getTimeInstance()
+ public static final DateFormat getTimeInstance()
{
return get(DEFAULT, 0, 1, Locale.getDefault(Locale.Category.FORMAT));
}
@@ -468,7 +499,7 @@
* SHORT for "h:mm a" in the US locale.
* @return a time formatter.
*/
- public final static DateFormat getTimeInstance(int style)
+ public static final DateFormat getTimeInstance(int style)
{
return get(style, 0, 1, Locale.getDefault(Locale.Category.FORMAT));
}
@@ -481,7 +512,7 @@
* @param aLocale the given locale.
* @return a time formatter.
*/
- public final static DateFormat getTimeInstance(int style,
+ public static final DateFormat getTimeInstance(int style,
Locale aLocale)
{
return get(style, 0, 1, aLocale);
@@ -497,7 +528,7 @@
* @see java.util.Locale.Category#FORMAT
* @return a date formatter.
*/
- public final static DateFormat getDateInstance()
+ public static final DateFormat getDateInstance()
{
return get(0, DEFAULT, 2, Locale.getDefault(Locale.Category.FORMAT));
}
@@ -514,7 +545,7 @@
* SHORT for "M/d/yy" in the US locale.
* @return a date formatter.
*/
- public final static DateFormat getDateInstance(int style)
+ public static final DateFormat getDateInstance(int style)
{
return get(0, style, 2, Locale.getDefault(Locale.Category.FORMAT));
}
@@ -527,7 +558,7 @@
* @param aLocale the given locale.
* @return a date formatter.
*/
- public final static DateFormat getDateInstance(int style,
+ public static final DateFormat getDateInstance(int style,
Locale aLocale)
{
return get(0, style, 2, aLocale);
@@ -543,7 +574,7 @@
* @see java.util.Locale.Category#FORMAT
* @return a date/time formatter.
*/
- public final static DateFormat getDateTimeInstance()
+ public static final DateFormat getDateTimeInstance()
{
return get(DEFAULT, DEFAULT, 3, Locale.getDefault(Locale.Category.FORMAT));
}
@@ -562,7 +593,7 @@
* SHORT for "h:mm a" in the US locale.
* @return a date/time formatter.
*/
- public final static DateFormat getDateTimeInstance(int dateStyle,
+ public static final DateFormat getDateTimeInstance(int dateStyle,
int timeStyle)
{
return get(timeStyle, dateStyle, 3, Locale.getDefault(Locale.Category.FORMAT));
@@ -576,7 +607,7 @@
* @param aLocale the given locale.
* @return a date/time formatter.
*/
- public final static DateFormat
+ public static final DateFormat
getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
{
return get(timeStyle, dateStyle, 3, aLocale);
@@ -588,7 +619,7 @@
*
* @return a date/time formatter
*/
- public final static DateFormat getInstance() {
+ public static final DateFormat getInstance() {
return getDateTimeInstance(SHORT, SHORT);
}
@@ -946,107 +977,107 @@
/**
* Constant identifying the era field.
*/
- public final static Field ERA = new Field("era", Calendar.ERA);
+ public static final Field ERA = new Field("era", Calendar.ERA);
/**
* Constant identifying the year field.
*/
- public final static Field YEAR = new Field("year", Calendar.YEAR);
+ public static final Field YEAR = new Field("year", Calendar.YEAR);
/**
* Constant identifying the month field.
*/
- public final static Field MONTH = new Field("month", Calendar.MONTH);
+ public static final Field MONTH = new Field("month", Calendar.MONTH);
/**
* Constant identifying the day of month field.
*/
- public final static Field DAY_OF_MONTH = new
+ public static final Field DAY_OF_MONTH = new
Field("day of month", Calendar.DAY_OF_MONTH);
/**
* Constant identifying the hour of day field, where the legal values
* are 1 to 24.
*/
- public final static Field HOUR_OF_DAY1 = new Field("hour of day 1",-1);
+ public static final Field HOUR_OF_DAY1 = new Field("hour of day 1",-1);
/**
* Constant identifying the hour of day field, where the legal values
* are 0 to 23.
*/
- public final static Field HOUR_OF_DAY0 = new
+ public static final Field HOUR_OF_DAY0 = new
Field("hour of day", Calendar.HOUR_OF_DAY);
/**
* Constant identifying the minute field.
*/
- public final static Field MINUTE =new Field("minute", Calendar.MINUTE);
+ public static final Field MINUTE =new Field("minute", Calendar.MINUTE);
/**
* Constant identifying the second field.
*/
- public final static Field SECOND =new Field("second", Calendar.SECOND);
+ public static final Field SECOND =new Field("second", Calendar.SECOND);
/**
* Constant identifying the millisecond field.
*/
- public final static Field MILLISECOND = new
+ public static final Field MILLISECOND = new
Field("millisecond", Calendar.MILLISECOND);
/**
* Constant identifying the day of week field.
*/
- public final static Field DAY_OF_WEEK = new
+ public static final Field DAY_OF_WEEK = new
Field("day of week", Calendar.DAY_OF_WEEK);
/**
* Constant identifying the day of year field.
*/
- public final static Field DAY_OF_YEAR = new
+ public static final Field DAY_OF_YEAR = new
Field("day of year", Calendar.DAY_OF_YEAR);
/**
* Constant identifying the day of week field.
*/
- public final static Field DAY_OF_WEEK_IN_MONTH =
+ public static final Field DAY_OF_WEEK_IN_MONTH =
new Field("day of week in month",
Calendar.DAY_OF_WEEK_IN_MONTH);
/**
* Constant identifying the week of year field.
*/
- public final static Field WEEK_OF_YEAR = new
+ public static final Field WEEK_OF_YEAR = new
Field("week of year", Calendar.WEEK_OF_YEAR);
/**
* Constant identifying the week of month field.
*/
- public final static Field WEEK_OF_MONTH = new
+ public static final Field WEEK_OF_MONTH = new
Field("week of month", Calendar.WEEK_OF_MONTH);
/**
* Constant identifying the time of day indicator
* (e.g. "a.m." or "p.m.") field.
*/
- public final static Field AM_PM = new
+ public static final Field AM_PM = new
Field("am pm", Calendar.AM_PM);
/**
* Constant identifying the hour field, where the legal values are
* 1 to 12.
*/
- public final static Field HOUR1 = new Field("hour 1", -1);
+ public static final Field HOUR1 = new Field("hour 1", -1);
/**
* Constant identifying the hour field, where the legal values are
* 0 to 11.
*/
- public final static Field HOUR0 = new
+ public static final Field HOUR0 = new
Field("hour", Calendar.HOUR);
/**
* Constant identifying the time zone field.
*/
- public final static Field TIME_ZONE = new Field("time zone", -1);
+ public static final Field TIME_ZONE = new Field("time zone", -1);
}
}
diff --git a/ojluni/src/main/java/java/text/DateFormatSymbols.java b/ojluni/src/main/java/java/text/DateFormatSymbols.java
index de10644..ba7464e 100644
--- a/ojluni/src/main/java/java/text/DateFormatSymbols.java
+++ b/ojluni/src/main/java/java/text/DateFormatSymbols.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,6 +54,9 @@
import libcore.icu.LocaleData;
import libcore.icu.TimeZoneNames;
+// Android-removed: Remove javadoc related to "rg" Locale extension.
+// The "rg" extension isn't supported until https://unicode-org.atlassian.net/browse/ICU-21831
+// is resolved, because java.text.* stack relies on ICU on resource resolution.
/**
* <code>DateFormatSymbols</code> is a public class for encapsulating
* localizable date-time formatting data, such as the names of the
@@ -98,6 +101,7 @@
* @see SimpleDateFormat
* @see java.util.SimpleTimeZone
* @author Chen-Lieh Huang
+ * @since 1.1
*/
public class DateFormatSymbols implements Serializable, Cloneable {
@@ -213,7 +217,7 @@
* </ul>
* The zone ID is <em>not</em> localized; it's one of the valid IDs of
* the {@link java.util.TimeZone TimeZone} class that are not
- * <a href="../java/util/TimeZone.html#CustomID">custom IDs</a>.
+ * <a href="../util/TimeZone.html#CustomID">custom IDs</a>.
* All other entries are localized names.
* @see java.util.TimeZone
* @serial
@@ -468,6 +472,12 @@
/**
* Gets month strings. For example: "January", "February", etc.
+ * An array with either 12 or 13 elements will be returned depending
+ * on whether or not {@link java.util.Calendar#UNDECIMBER Calendar.UNDECIMBER}
+ * is supported. Use
+ * {@link java.util.Calendar#JANUARY Calendar.JANUARY},
+ * {@link java.util.Calendar#FEBRUARY Calendar.FEBRUARY},
+ * etc. to index the result array.
*
* <p>If the language requires different forms for formatting and
* stand-alone usages, this method returns month names in the
@@ -479,6 +489,8 @@
* Calendar Elements in the Unicode Locale Data Markup Language
* (LDML) specification</a> for more details.
*
+ * @implSpec This method returns 13 elements since
+ * {@link java.util.Calendar#UNDECIMBER Calendar.UNDECIMBER} is supported.
* @return the month strings.
*/
public String[] getMonths() {
@@ -487,7 +499,9 @@
/**
* Sets month strings. For example: "January", "February", etc.
- * @param newMonths the new month strings.
+ * @param newMonths the new month strings. The array should
+ * be indexed by {@link java.util.Calendar#JANUARY Calendar.JANUARY},
+ * {@link java.util.Calendar#FEBRUARY Calendar.FEBRUARY}, etc.
*/
public void setMonths(String[] newMonths) {
months = Arrays.copyOf(newMonths, newMonths.length);
@@ -496,9 +510,15 @@
/**
* Gets short month strings. For example: "Jan", "Feb", etc.
+ * An array with either 12 or 13 elements will be returned depending
+ * on whether or not {@link java.util.Calendar#UNDECIMBER Calendar.UNDECIMBER}
+ * is supported. Use
+ * {@link java.util.Calendar#JANUARY Calendar.JANUARY},
+ * {@link java.util.Calendar#FEBRUARY Calendar.FEBRUARY},
+ * etc. to index the result array.
*
* <p>If the language requires different forms for formatting and
- * stand-alone usages, This method returns short month names in
+ * stand-alone usages, this method returns short month names in
* the formatting form. For example, the preferred abbreviation
* for January in the Catalan language is <em>de gen.</em> in the
* formatting form, while it is <em>gen.</em> in the stand-alone
@@ -507,6 +527,8 @@
* Calendar Elements in the Unicode Locale Data Markup Language
* (LDML) specification</a> for more details.
*
+ * @implSpec This method returns 13 elements since
+ * {@link java.util.Calendar#UNDECIMBER Calendar.UNDECIMBER} is supported.
* @return the short month strings.
*/
public String[] getShortMonths() {
@@ -515,7 +537,9 @@
/**
* Sets short month strings. For example: "Jan", "Feb", etc.
- * @param newShortMonths the new short month strings.
+ * @param newShortMonths the new short month strings. The array should
+ * be indexed by {@link java.util.Calendar#JANUARY Calendar.JANUARY},
+ * {@link java.util.Calendar#FEBRUARY Calendar.FEBRUARY}, etc.
*/
public void setShortMonths(String[] newShortMonths) {
shortMonths = Arrays.copyOf(newShortMonths, newShortMonths.length);
@@ -524,8 +548,10 @@
/**
* Gets weekday strings. For example: "Sunday", "Monday", etc.
- * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
- * <code>Calendar.MONDAY</code>, etc. to index the result array.
+ * @return the weekday strings. Use
+ * {@link java.util.Calendar#SUNDAY Calendar.SUNDAY},
+ * {@link java.util.Calendar#MONDAY Calendar.MONDAY}, etc. to index
+ * the result array.
*/
public String[] getWeekdays() {
return Arrays.copyOf(weekdays, weekdays.length);
@@ -534,8 +560,8 @@
/**
* Sets weekday strings. For example: "Sunday", "Monday", etc.
* @param newWeekdays the new weekday strings. The array should
- * be indexed by <code>Calendar.SUNDAY</code>,
- * <code>Calendar.MONDAY</code>, etc.
+ * be indexed by {@link java.util.Calendar#SUNDAY Calendar.SUNDAY},
+ * {@link java.util.Calendar#MONDAY Calendar.MONDAY}, etc.
*/
public void setWeekdays(String[] newWeekdays) {
weekdays = Arrays.copyOf(newWeekdays, newWeekdays.length);
@@ -544,8 +570,10 @@
/**
* Gets short weekday strings. For example: "Sun", "Mon", etc.
- * @return the short weekday strings. Use <code>Calendar.SUNDAY</code>,
- * <code>Calendar.MONDAY</code>, etc. to index the result array.
+ * @return the short weekday strings. Use
+ * {@link java.util.Calendar#SUNDAY Calendar.SUNDAY},
+ * {@link java.util.Calendar#MONDAY Calendar.MONDAY}, etc. to index
+ * the result array.
*/
public String[] getShortWeekdays() {
return Arrays.copyOf(shortWeekdays, shortWeekdays.length);
@@ -554,8 +582,8 @@
/**
* Sets short weekday strings. For example: "Sun", "Mon", etc.
* @param newShortWeekdays the new short weekday strings. The array should
- * be indexed by <code>Calendar.SUNDAY</code>,
- * <code>Calendar.MONDAY</code>, etc.
+ * be indexed by {@link java.util.Calendar#SUNDAY Calendar.SUNDAY},
+ * {@link java.util.Calendar#MONDAY Calendar.MONDAY}, etc.
*/
public void setShortWeekdays(String[] newShortWeekdays) {
shortWeekdays = Arrays.copyOf(newShortWeekdays, newShortWeekdays.length);
@@ -750,7 +778,9 @@
// Android-changed: Don't include zone strings in hashCode to avoid populating it.
// hashCode = 11 * hashCode + Arrays.deepHashCode(getZoneStringsWrapper());
hashCode = 11 * hashCode + Objects.hashCode(localPatternChars);
- cachedHashCode = hashCode;
+ if (hashCode != 0) {
+ cachedHashCode = hashCode;
+ }
}
return hashCode;
@@ -805,12 +835,12 @@
private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
= new ConcurrentHashMap<>(3);
- private transient int lastZoneIndex = 0;
+ private transient int lastZoneIndex;
/**
* Cached hash code
*/
- transient volatile int cachedHashCode = 0;
+ transient volatile int cachedHashCode;
// Android-changed: update comment to describe local modification.
/**
diff --git a/ojluni/src/main/java/java/text/DecimalFormat.java b/ojluni/src/main/java/java/text/DecimalFormat.java
index 1dc430c..2917c23 100644
--- a/ojluni/src/main/java/java/text/DecimalFormat.java
+++ b/ojluni/src/main/java/java/text/DecimalFormat.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -172,75 +172,79 @@
* are not localized.
*
* <blockquote>
- * <table border=0 cellspacing=3 cellpadding=0 summary="Chart showing symbol,
- * location, localized, and meaning.">
- * <tr style="background-color: rgb(204, 204, 255);">
- * <th align=left>Symbol
- * <th align=left>Location
- * <th align=left>Localized?
- * <th align=left>Meaning
- * <tr valign=top>
- * <td><code>0</code>
+ * <table class="striped">
+ * <caption style="display:none">Chart showing symbol, location, localized, and meaning.</caption>
+ * <thead>
+ * <tr>
+ * <th scope="col" style="text-align:left">Symbol
+ * <th scope="col" style="text-align:left">Location
+ * <th scope="col" style="text-align:left">Localized?
+ * <th scope="col" style="text-align:left">Meaning
+ * </thead>
+ * <tbody>
+ * <tr style="vertical-align:top">
+ * <th scope="row"><code>0</code>
* <td>Number
* <td>Yes
* <td>Digit
- * <tr style="vertical-align: top; background-color: rgb(238, 238, 255);">
- * <td><code>#</code>
+ * <tr style="vertical-align: top">
+ * <th scope="row"><code>#</code>
* <td>Number
* <td>Yes
* <td>Digit, zero shows as absent
- * <tr valign=top>
- * <td><code>.</code>
+ * <tr style="vertical-align:top">
+ * <th scope="row"><code>.</code>
* <td>Number
* <td>Yes
* <td>Decimal separator or monetary decimal separator
- * <tr style="vertical-align: top; background-color: rgb(238, 238, 255);">
- * <td><code>-</code>
+ * <tr style="vertical-align: top">
+ * <th scope="row"><code>-</code>
* <td>Number
* <td>Yes
* <td>Minus sign
- * <tr valign=top>
- * <td><code>,</code>
+ * <tr style="vertical-align:top">
+ * <th scope="row"><code>,</code>
* <td>Number
* <td>Yes
* <td>Grouping separator
- * <tr style="vertical-align: top; background-color: rgb(238, 238, 255);">
- * <td><code>E</code>
+ * <tr style="vertical-align: top">
+ * <th scope="row"><code>E</code>
* <td>Number
* <td>Yes
* <td>Separates mantissa and exponent in scientific notation.
* <em>Need not be quoted in prefix or suffix.</em>
- * <tr valign=top>
- * <td><code>;</code>
+ * <tr style="vertical-align:top">
+ * <th scope="row"><code>;</code>
* <td>Subpattern boundary
* <td>Yes
* <td>Separates positive and negative subpatterns
- * <tr style="vertical-align: top; background-color: rgb(238, 238, 255);">
- * <td><code>%</code>
+ * <tr style="vertical-align: top">
+ * <th scope="row"><code>%</code>
* <td>Prefix or suffix
* <td>Yes
* <td>Multiply by 100 and show as percentage
- * <tr valign=top>
- * <td><code>\u2030</code>
+ * <tr style="vertical-align:top">
+ * <th scope="row"><code>\u2030</code>
* <td>Prefix or suffix
* <td>Yes
* <td>Multiply by 1000 and show as per mille value
- * <tr style="vertical-align: top; background-color: rgb(238, 238, 255);">
- * <td><code>¤</code> (<code>\u00A4</code>)
+ * <tr style="vertical-align: top">
+ * <th scope="row"><code>¤</code> (<code>\u00A4</code>)
* <td>Prefix or suffix
* <td>No
* <td>Currency sign, replaced by currency symbol. If
* doubled, replaced by international currency symbol.
* If present in a pattern, the monetary decimal separator
* is used instead of the decimal separator.
- * <tr valign=top>
- * <td><code>'</code>
+ * <tr style="vertical-align:top">
+ * <th scope="row"><code>'</code>
* <td>Prefix or suffix
* <td>No
* <td>Used to quote special characters in a prefix or suffix,
* for example, <code>"'#'#"</code> formats 123 to
* <code>"#123"</code>. To create a single quote
* itself, use two in a row: <code>"# o''clock"</code>.
+ * </tbody>
* </table>
* </blockquote>
*
@@ -327,7 +331,7 @@
* and <code>isParseIntegerOnly()</code> are false.
* </ul>
*
- * <h4><a name="synchronization">Synchronization</a></h4>
+ * <h4><a id="synchronization">Synchronization</a></h4>
*
* <p>
* Decimal formats are generally not synchronized.
@@ -371,12 +375,13 @@
* }
* }</pre></blockquote>
*
- * @see <a href="https://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html">Java Tutorial</a>
+ * @see <a href="http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html">Java Tutorial</a>
* @see NumberFormat
* @see DecimalFormatSymbols
* @see ParsePosition
* @author Mark Davis
* @author Alan Liu
+ * @since 1.1
*/
public class DecimalFormat extends NumberFormat {
@@ -608,8 +613,14 @@
* @param number the number to format
* @param toAppendTo the <code>StringBuffer</code> to which the formatted
* text is to be appended
- * @param pos On input: an alignment field, if desired.
- * On output: the offsets of the alignment field.
+ * @param pos keeps track on the position of the field within the
+ * returned string. For example, for formatting a number
+ * {@code 1234567.89} in {@code Locale.US} locale,
+ * if the given {@code fieldPosition} is
+ * {@link NumberFormat#INTEGER_FIELD}, the begin index
+ * and end index of {@code fieldPosition} will be set
+ * to 0 and 9, respectively for the output string
+ * {@code 1,234,567.89}.
* @return the value passed in as <code>toAppendTo</code>
* @exception IllegalArgumentException if <code>number</code> is
* null or not an instance of <code>Number</code>.
@@ -645,8 +656,16 @@
* Formats a double to produce a string.
* @param number The double to format
* @param result where the text is to be appended
- * @param fieldPosition On input: an alignment field, if desired.
- * On output: the offsets of the alignment field.
+ * @param fieldPosition keeps track on the position of the field within
+ * the returned string. For example, for formatting
+ * a number {@code 1234567.89} in {@code Locale.US}
+ * locale, if the given {@code fieldPosition} is
+ * {@link NumberFormat#INTEGER_FIELD}, the begin index
+ * and end index of {@code fieldPosition} will be set
+ * to 0 and 9, respectively for the output string
+ * {@code 1,234,567.89}.
+ * @exception NullPointerException if {@code result} or
+ * {@code fieldPosition} is {@code null}
* @exception ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
* @return The formatted number string
@@ -778,8 +797,16 @@
* Format a long to produce a string.
* @param number The long to format
* @param result where the text is to be appended
- * @param fieldPosition On input: an alignment field, if desired.
- * On output: the offsets of the alignment field.
+ * @param fieldPosition keeps track on the position of the field within
+ * the returned string. For example, for formatting
+ * a number {@code 123456789} in {@code Locale.US}
+ * locale, if the given {@code fieldPosition} is
+ * {@link NumberFormat#INTEGER_FIELD}, the begin index
+ * and end index of {@code fieldPosition} will be set
+ * to 0 and 11, respectively for the output string
+ * {@code 123,456,789}.
+ * @exception NullPointerException if {@code result} or
+ * {@code fieldPosition} is {@code null}
* @exception ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
* @return The formatted number string
@@ -881,8 +908,14 @@
* Formats a BigDecimal to produce a string.
* @param number The BigDecimal to format
* @param result where the text is to be appended
- * @param fieldPosition On input: an alignment field, if desired.
- * On output: the offsets of the alignment field.
+ * @param fieldPosition keeps track on the position of the field within
+ * the returned string. For example, for formatting
+ * a number {@code 1234567.89} in {@code Locale.US}
+ * locale, if the given {@code fieldPosition} is
+ * {@link NumberFormat#INTEGER_FIELD}, the begin index
+ * and end index of {@code fieldPosition} will be set
+ * to 0 and 9, respectively for the output string
+ * {@code 1,234,567.89}.
* @return The formatted number string
* @exception ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
@@ -949,8 +982,14 @@
* Format a BigInteger to produce a string.
* @param number The BigInteger to format
* @param result where the text is to be appended
- * @param fieldPosition On input: an alignment field, if desired.
- * On output: the offsets of the alignment field.
+ * @param fieldPosition keeps track on the position of the field within
+ * the returned string. For example, for formatting
+ * a number {@code 123456789} in {@code Locale.US}
+ * locale, if the given {@code fieldPosition} is
+ * {@link NumberFormat#INTEGER_FIELD}, the begin index
+ * and end index of {@code fieldPosition} will be set
+ * to 0 and 11, respectively for the output string
+ * {@code 123,456,789}.
* @return The formatted number string
* @exception ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
@@ -1200,7 +1239,8 @@
* Decimal : min = 0. max = 3.
*
*
- private void checkAndSetFastPathStatus() {
+ *
+ private boolean checkAndSetFastPathStatus() {
boolean fastPathWasOn = isFastPath;
@@ -1230,12 +1270,27 @@
} else
isFastPath = false;
+ resetFastPathData(fastPathWasOn);
+ fastPathCheckNeeded = false;
+
+ /*
+ * Returns true after successfully checking the fast path condition and
+ * setting the fast path data. The return value is used by the
+ * fastFormat() method to decide whether to call the resetFastPathData
+ * method to reinitialize fast path data or is it already initialized
+ * in this method.
+ *
+ return true;
+ }
+
+ private void resetFastPathData(boolean fastPathWasOn) {
// Since some instance properties may have changed while still falling
// in the fast-path case, we need to reinitialize fastPathData anyway.
if (isFastPath) {
// We need to instantiate fastPathData if not already done.
- if (fastPathData == null)
+ if (fastPathData == null) {
fastPathData = new FastPathData();
+ }
// Sets up the locale specific constants used when formatting.
// '0' is our default representation of zero.
@@ -1243,22 +1298,25 @@
fastPathData.groupingChar = symbols.getGroupingSeparator();
// Sets up fractional constants related to currency/decimal pattern.
- fastPathData.fractionalMaxIntBound = (isCurrencyFormat) ? 99 : 999;
- fastPathData.fractionalScaleFactor = (isCurrencyFormat) ? 100.0d : 1000.0d;
+ fastPathData.fractionalMaxIntBound = (isCurrencyFormat)
+ ? 99 : 999;
+ fastPathData.fractionalScaleFactor = (isCurrencyFormat)
+ ? 100.0d : 1000.0d;
// Records the need for adding prefix or suffix
- fastPathData.positiveAffixesRequired =
- (positivePrefix.length() != 0) || (positiveSuffix.length() != 0);
- fastPathData.negativeAffixesRequired =
- (negativePrefix.length() != 0) || (negativeSuffix.length() != 0);
+ fastPathData.positiveAffixesRequired
+ = !positivePrefix.isEmpty() || !positiveSuffix.isEmpty();
+ fastPathData.negativeAffixesRequired
+ = !negativePrefix.isEmpty() || !negativeSuffix.isEmpty();
// Creates a cached char container for result, with max possible size.
int maxNbIntegralDigits = 10;
int maxNbGroups = 3;
- int containerSize =
- Math.max(positivePrefix.length(), negativePrefix.length()) +
- maxNbIntegralDigits + maxNbGroups + 1 + maximumFractionDigits +
- Math.max(positiveSuffix.length(), negativeSuffix.length());
+ int containerSize
+ = Math.max(positivePrefix.length(), negativePrefix.length())
+ + maxNbIntegralDigits + maxNbGroups + 1
+ + maximumFractionDigits
+ + Math.max(positiveSuffix.length(), negativeSuffix.length());
fastPathData.fastPathContainer = new char[containerSize];
@@ -1270,17 +1328,18 @@
// Sets up fixed index positions for integral and fractional digits.
// Sets up decimal point in cached result container.
- int longestPrefixLength =
- Math.max(positivePrefix.length(), negativePrefix.length());
- int decimalPointIndex =
- maxNbIntegralDigits + maxNbGroups + longestPrefixLength;
+ int longestPrefixLength
+ = Math.max(positivePrefix.length(),
+ negativePrefix.length());
+ int decimalPointIndex
+ = maxNbIntegralDigits + maxNbGroups + longestPrefixLength;
- fastPathData.integralLastIndex = decimalPointIndex - 1;
+ fastPathData.integralLastIndex = decimalPointIndex - 1;
fastPathData.fractionalFirstIndex = decimalPointIndex + 1;
- fastPathData.fastPathContainer[decimalPointIndex] =
- isCurrencyFormat ?
- symbols.getMonetaryDecimalSeparator() :
- symbols.getDecimalSeparator();
+ fastPathData.fastPathContainer[decimalPointIndex]
+ = isCurrencyFormat
+ ? symbols.getMonetaryDecimalSeparator()
+ : symbols.getDecimalSeparator();
} else if (fastPathWasOn) {
// Previous state was fast-path and is no more.
@@ -1291,8 +1350,6 @@
fastPathData.charsPositivePrefix = null;
fastPathData.charsNegativePrefix = null;
}
-
- fastPathCheckNeeded = false;
}
/**
@@ -1786,9 +1843,11 @@
* @return the formatted result for {@code d} as a string.
*
String fastFormat(double d) {
+ boolean isDataSet = false;
// (Re-)Evaluates fast-path status if needed.
- if (fastPathCheckNeeded)
- checkAndSetFastPathStatus();
+ if (fastPathCheckNeeded) {
+ isDataSet = checkAndSetFastPathStatus();
+ }
if (!isFastPath )
// DecimalFormat instance is not in a fast-path state.
@@ -1812,9 +1871,21 @@
if (d > MAX_INT_AS_DOUBLE)
// Filters out values that are outside expected fast-path range
return null;
- else
+ else {
+ if (!isDataSet) {
+ /*
+ * If the fast path data is not set through
+ * checkAndSetFastPathStatus() and fulfil the
+ * fast path conditions then reset the data
+ * directly through resetFastPathData()
+ *
+ resetFastPathData(isFastPath);
+ }
fastDoubleFormat(d, negative);
+ }
+
+
// Returns a new string from updated fastPathContainer.
return new String(fastPathData.fastPathContainer,
fastPathData.firstUsedIndex,
@@ -2150,7 +2221,7 @@
Format.Field signAttribute) {
int start = result.length();
- if (string.length() > 0) {
+ if (!string.isEmpty()) {
result.append(string);
for (int counter = 0, max = positions.length; counter < max;
counter++) {
@@ -2233,7 +2304,7 @@
// special case NaN
if (text.regionMatches(pos.index, symbols.getNaN(), 0, symbols.getNaN().length())) {
pos.index = pos.index + symbols.getNaN().length();
- return new Double(Double.NaN);
+ return Double.valueOf(Double.NaN);
}
boolean[] status = new boolean[STATUS_LENGTH];
@@ -2244,19 +2315,19 @@
// special case INFINITY
if (status[STATUS_INFINITE]) {
if (status[STATUS_POSITIVE] == (multiplier >= 0)) {
- return new Double(Double.POSITIVE_INFINITY);
+ return Double.valueOf(Double.POSITIVE_INFINITY);
} else {
- return new Double(Double.NEGATIVE_INFINITY);
+ return Double.valueOf(Double.NEGATIVE_INFINITY);
}
}
if (multiplier == 0) {
if (digitList.isZero()) {
- return new Double(Double.NaN);
+ return Double.valueOf(Double.NaN);
} else if (status[STATUS_POSITIVE]) {
- return new Double(Double.POSITIVE_INFINITY);
+ return Double.valueOf(Double.POSITIVE_INFINITY);
} else {
- return new Double(Double.NEGATIVE_INFINITY);
+ return Double.valueOf(Double.NEGATIVE_INFINITY);
}
}
@@ -2330,8 +2401,8 @@
!isParseIntegerOnly();
}
- return gotDouble ?
- (Number)new Double(doubleResult) : (Number)new Long(longResult);
+ // cast inside of ?: because of binary numeric promotion, JLS 15.25
+ return gotDouble ? (Number)doubleResult : (Number)longResult;
}
*/
if (pos.index < 0 || pos.index >= text.length()) {
@@ -3306,7 +3377,7 @@
} else {
string = symbols.getCurrencySymbol();
}
- if (string.length() > 0) {
+ if (!string.isEmpty()) {
if (positions == null) {
positions = new ArrayList<>(2);
}
@@ -3603,13 +3674,6 @@
isCurrencyFormat = false;
useExponentialNotation = false;
- // Two variables are used to record the subrange of the pattern
- // occupied by phase 1. This is used during the processing of the
- // second pattern (the one representing negative numbers) to ensure
- // that no deviation exists in phase 1 between the two patterns.
- int phaseOneStart = 0;
- int phaseOneLength = 0;
-
int start = 0;
for (int j = 1; j >= 0 && start < pattern.length(); --j) {
boolean inQuote = false;
@@ -3660,9 +3724,6 @@
ch == groupingSeparator ||
ch == decimalSeparator) {
phase = 1;
- if (j == 1) {
- phaseOneStart = pos;
- }
--pos; // Reprocess this character
continue;
} else if (ch == CURRENCY_SIGN) {
@@ -3733,17 +3794,29 @@
break;
case 1:
- // Phase one must be identical in the two sub-patterns. We
- // enforce this by doing a direct comparison. While
- // processing the first sub-pattern, we just record its
- // length. While processing the second, we compare
- // characters.
- if (j == 1) {
- ++phaseOneLength;
- } else {
- if (--phaseOneLength == 0) {
- phase = 2;
- affix = suffix;
+ // The negative subpattern (j = 0) serves only to specify the
+ // negative prefix and suffix, so all the phase 1 characters
+ // e.g. digits, zeroDigit, groupingSeparator,
+ // decimalSeparator, exponent are ignored
+ if (j == 0) {
+ while (pos < pattern.length()) {
+ char negPatternChar = pattern.charAt(pos);
+ if (negPatternChar == digit
+ || negPatternChar == zeroDigit
+ || negPatternChar == groupingSeparator
+ || negPatternChar == decimalSeparator) {
+ ++pos;
+ } else if (pattern.regionMatches(pos, exponent,
+ 0, exponent.length())) {
+ pos = pos + exponent.length();
+ } else {
+ // Not a phase 1 character, consider it as
+ // suffix and parse it in phase 2
+ --pos; //process it again in outer loop
+ phase = 2;
+ affix = suffix;
+ break;
+ }
}
continue;
}
@@ -3797,7 +3870,6 @@
while (pos < pattern.length() &&
pattern.charAt(pos) == zeroDigit) {
++minExponentDigits;
- ++phaseOneLength;
++pos;
}
@@ -3816,7 +3888,6 @@
phase = 2;
affix = suffix;
--pos;
- --phaseOneLength;
continue;
}
break;
@@ -3887,7 +3958,7 @@
}
}
- if (pattern.length() == 0) {
+ if (pattern.isEmpty()) {
posPrefixPattern = posSuffixPattern = "";
setMinimumIntegerDigits(0);
setMaximumIntegerDigits(MAXIMUM_INTEGER_DIGITS);
diff --git a/ojluni/src/main/java/java/text/DecimalFormatSymbols.java b/ojluni/src/main/java/java/text/DecimalFormatSymbols.java
index a65a079..9bb4e90 100644
--- a/ojluni/src/main/java/java/text/DecimalFormatSymbols.java
+++ b/ojluni/src/main/java/java/text/DecimalFormatSymbols.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,9 @@
import libcore.icu.ICU;
import libcore.icu.LocaleData;
+// Android-removed: Remove javadoc related to "rg" Locale extension.
+// The "rg" extension isn't supported until https://unicode-org.atlassian.net/browse/ICU-21831
+// is resolved, because java.text.* stack relies on ICU on resource resolution.
/**
* This class represents the set of symbols (such as the decimal separator,
* the grouping separator, and so on) needed by <code>DecimalFormat</code>
@@ -61,6 +64,7 @@
* @see DecimalFormat
* @author Mark Davis
* @author Alan Liu
+ * @since 1.1
*/
public class DecimalFormatSymbols implements Cloneable, Serializable {
@@ -398,6 +402,7 @@
*/
public String getCurrencySymbol()
{
+ initializeCurrency(locale);
return currencySymbol;
}
@@ -410,6 +415,7 @@
*/
public void setCurrencySymbol(String currency)
{
+ initializeCurrency(locale);
currencySymbol = currency;
// Android-added: reset cachedIcuDFS.
cachedIcuDFS = null;
@@ -424,6 +430,7 @@
*/
public String getInternationalCurrencySymbol()
{
+ initializeCurrency(locale);
return intlCurrencySymbol;
}
@@ -445,6 +452,7 @@
*/
public void setInternationalCurrencySymbol(String currencyCode)
{
+ initializeCurrency(locale);
intlCurrencySymbol = currencyCode;
currency = null;
if (currencyCode != null) {
@@ -468,6 +476,7 @@
* @since 1.4
*/
public Currency getCurrency() {
+ initializeCurrency(locale);
return currency;
}
@@ -487,6 +496,7 @@
if (currency == null) {
throw new NullPointerException();
}
+ initializeCurrency(locale);
this.currency = currency;
intlCurrencySymbol = currency.getCurrencyCode();
currencySymbol = currency.getSymbol(locale);
@@ -530,14 +540,15 @@
{
return exponential;
}
- /**
- * Returns the string used to separate the mantissa from the exponent.
- * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
- *
- * @return the exponent separator string
- * @see #setExponentSeparator(java.lang.String)
- * @since 1.6
- */
+
+ /**
+ * Returns the string used to separate the mantissa from the exponent.
+ * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
+ *
+ * @return the exponent separator string
+ * @see #setExponentSeparator(java.lang.String)
+ * @since 1.6
+ */
public String getExponentSeparator()
{
return exponentialSeparator;
@@ -553,22 +564,22 @@
cachedIcuDFS = null;
}
- /**
- * Sets the string used to separate the mantissa from the exponent.
- * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
- *
- * @param exp the exponent separator string
- * @exception NullPointerException if <code>exp</code> is null
- * @see #getExponentSeparator()
- * @since 1.6
- */
+ /**
+ * Sets the string used to separate the mantissa from the exponent.
+ * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
+ *
+ * @param exp the exponent separator string
+ * @exception NullPointerException if <code>exp</code> is null
+ * @see #getExponentSeparator()
+ * @since 1.6
+ */
public void setExponentSeparator(String exp)
{
if (exp == null) {
throw new NullPointerException();
}
exponentialSeparator = exp;
- }
+ }
//------------------------------------------------------------
@@ -607,7 +618,7 @@
patternSeparator == other.patternSeparator &&
infinity.equals(other.infinity) &&
NaN.equals(other.NaN) &&
- currencySymbol.equals(other.currencySymbol) &&
+ getCurrencySymbol().equals(other.getCurrencySymbol()) && // possible currency init occurs here
intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
currency == other.currency &&
monetarySeparator == other.monetarySeparator &&
@@ -631,9 +642,6 @@
result = result * 37 + patternSeparator;
result = result * 37 + infinity.hashCode();
result = result * 37 + NaN.hashCode();
- result = result * 37 + currencySymbol.hashCode();
- result = result * 37 + intlCurrencySymbol.hashCode();
- result = result * 37 + currency.hashCode();
result = result * 37 + monetarySeparator;
result = result * 37 + exponentialSeparator.hashCode();
result = result * 37 + locale.hashCode();
@@ -649,20 +657,25 @@
// BEGIN Android-changed: Removed use of DecimalFormatSymbolsProvider. Switched to ICU.
/*
+ // check for region override
+ Locale override = locale.getUnicodeLocaleType("nu") == null ?
+ CalendarDataUtility.findRegionOverride(locale) :
+ locale;
+
// get resource bundle data
- LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
+ LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, override);
// Avoid potential recursions
if (!(adapter instanceof ResourceBundleBasedAdapter)) {
adapter = LocaleProviderAdapter.getResourceBundleBased();
}
- Object[] data = adapter.getLocaleResources(locale).getDecimalFormatSymbolsData();
+ Object[] data = adapter.getLocaleResources(override).getDecimalFormatSymbolsData();
+ String[] numberElements = (String[]) data[0];
*/
if (locale == null) {
throw new NullPointerException("locale");
}
locale = LocaleData.mapInvalidAndNullLocales(locale);
LocaleData localeData = LocaleData.get(locale);
- Object[] data = new Object[3];
String[] values = new String[11];
values[0] = String.valueOf(localeData.decimalSeparator);
values[1] = String.valueOf(localeData.groupingSeparator);
@@ -675,11 +688,9 @@
values[8] = localeData.perMill;
values[9] = localeData.infinity;
values[10] = localeData.NaN;
- data[0] = values;
+ String[] numberElements = values;
// END Android-changed: Removed use of DecimalFormatSymbolsProvider. Switched to ICU.
- String[] numberElements = (String[]) data[0];
-
// Android-changed: Added maybeStripMarkers
decimalSeparator = numberElements[0].charAt(0);
groupingSeparator = numberElements[1].charAt(0);
@@ -694,18 +705,51 @@
infinity = numberElements[9];
NaN = numberElements[10];
+ // Android-removed: Removed use of DecimalFormatSymbolsProvider. Switched to ICU.
+ // Upstream tries to re-use the strings from the cache, but Android doesn't have
+ // LocaleProviderAdapter to cache the strings.
+ // intlCurrencySymbol = (String) data[1];
+ // currencySymbol = (String) data[2];
+
+ // Currently the monetary decimal separator is the same as the
+ // standard decimal separator for all locales that we support.
+ // If that changes, add a new entry to NumberElements.
+ monetarySeparator = decimalSeparator;
+ }
+
+ /**
+ * Lazy initialization for currency related fields
+ */
+ private void initializeCurrency(Locale locale) {
+ if (currencyInitialized) {
+ return;
+ }
+
// Try to obtain the currency used in the locale's country.
// Check for empty country string separately because it's a valid
// country ID for Locale (and used for the C locale), but not a valid
// ISO 3166 country code, and exceptions are expensive.
- if (locale.getCountry().length() > 0) {
+ if (!locale.getCountry().isEmpty()) {
try {
currency = Currency.getInstance(locale);
} catch (IllegalArgumentException e) {
// use default values below for compatibility
}
}
+
if (currency != null) {
+ // BEGIN Android-changed: Removed use of DecimalFormatSymbolsProvider. Switched to ICU.
+ // Android doesn't have DecimalFormatSymbolsProvider to cache the values.
+ // Thus, simplify the code not loading from the cache.
+ /*
+ // get resource bundle data
+ LocaleProviderAdapter adapter =
+ LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
+ // Avoid potential recursions
+ if (!(adapter instanceof ResourceBundleBasedAdapter)) {
+ adapter = LocaleProviderAdapter.getResourceBundleBased();
+ }
+ Object[] data = adapter.getLocaleResources(locale).getDecimalFormatSymbolsData();
intlCurrencySymbol = currency.getCurrencyCode();
if (data[1] != null && data[1] == intlCurrencySymbol) {
currencySymbol = (String) data[2];
@@ -714,6 +758,10 @@
data[1] = intlCurrencySymbol;
data[2] = currencySymbol;
}
+ */
+ intlCurrencySymbol = currency.getCurrencyCode();
+ currencySymbol = currency.getSymbol(locale);
+ // END Android-changed: Removed use of DecimalFormatSymbolsProvider. Switched to ICU.
} else {
// default values
intlCurrencySymbol = "XXX";
@@ -723,10 +771,8 @@
}
currencySymbol = "\u00A4";
}
- // Currently the monetary decimal separator is the same as the
- // standard decimal separator for all locales that we support.
- // If that changes, add a new entry to NumberElements.
- monetarySeparator = decimalSeparator;
+
+ currencyInitialized = true;
}
// Android-changed: maybeStripMarkers added in b/26207216, fixed in b/32465689.
@@ -772,6 +818,7 @@
return cachedIcuDFS;
}
+ initializeCurrency(this.locale);
cachedIcuDFS = new android.icu.text.DecimalFormatSymbols(this.locale);
// Do not localize plus sign. See "Special Pattern Characters" section in DecimalFormat.
// http://b/67034519
@@ -804,7 +851,7 @@
try {
cachedIcuDFS.setCurrency(
- android.icu.util.Currency.getInstance(currency.getCurrencyCode()));
+ android.icu.util.Currency.getInstance(getCurrency().getCurrencyCode()));
} catch (NullPointerException e) {
currency = Currency.getInstance("XXX");
}
@@ -918,7 +965,7 @@
* default serialization will work properly if this object is streamed out again.
* Initializes the currency from the intlCurrencySymbol field.
*
- * @since JDK 1.1.6
+ * @since 1.1.6
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
@@ -971,10 +1018,13 @@
setExponentSeparator((String) fields.get("exponentialSeparator", "E"));
}
- try {
- currency = Currency.getInstance(intlCurrencySymbol);
- } catch (IllegalArgumentException e) {
- currency = null;
+ if (intlCurrencySymbol != null) {
+ try {
+ currency = Currency.getInstance(intlCurrencySymbol);
+ currencyInitialized = true;
+ } catch (IllegalArgumentException e) {
+ currency = null;
+ }
}
// END Android-changed: Android specific serialization code.
}
@@ -1073,7 +1123,7 @@
/**
* The decimal separator used when formatting currency values.
* @serial
- * @since JDK 1.1.6
+ * @since 1.1.6
* @see #getMonetaryDecimalSeparator
*/
private char monetarySeparator; // Field new in JDK 1.1.6
@@ -1087,20 +1137,20 @@
* The intent is that this will be added to the API in the future.
*
* @serial
- * @since JDK 1.1.6
+ * @since 1.1.6
*/
private char exponential; // Field new in JDK 1.1.6
- /**
- * The string used to separate the mantissa from the exponent.
- * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
- * <p>
- * If both <code>exponential</code> and <code>exponentialSeparator</code>
- * exist, this <code>exponentialSeparator</code> has the precedence.
- *
- * @serial
- * @since 1.6
- */
+ /**
+ * The string used to separate the mantissa from the exponent.
+ * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
+ * <p>
+ * If both <code>exponential</code> and <code>exponentialSeparator</code>
+ * exist, this <code>exponentialSeparator</code> has the precedence.
+ *
+ * @serial
+ * @since 1.6
+ */
private String exponentialSeparator; // Field new in JDK 1.6
/**
@@ -1113,6 +1163,7 @@
// currency; only the ISO code is serialized.
private transient Currency currency;
+ private transient volatile boolean currencyInitialized;
// Proclaim JDK 1.1 FCS compatibility
static final long serialVersionUID = 5772796243397350300L;
@@ -1143,7 +1194,7 @@
* is always written.
*
* @serial
- * @since JDK 1.1.6
+ * @since 1.1.6
*/
private int serialVersionOnStream = currentSerialVersion;
diff --git a/ojluni/src/main/java/java/text/MessageFormat.java b/ojluni/src/main/java/java/text/MessageFormat.java
index 4a48e9c..bcf115f 100644
--- a/ojluni/src/main/java/java/text/MessageFormat.java
+++ b/ojluni/src/main/java/java/text/MessageFormat.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -69,7 +69,7 @@
* behavior is defined by the pattern that you provide as well as the
* subformats used for inserted arguments.
*
- * <h3><a name="patterns">Patterns and Their Interpretation</a></h3>
+ * <h3><a id="patterns">Patterns and Their Interpretation</a></h3>
*
* <code>MessageFormat</code> uses patterns of the following form:
* <blockquote><pre>
@@ -147,73 +147,78 @@
* shown in the table are illegal. A <i>SubformatPattern</i> must
* be a valid pattern string for the {@code Format} subclass used.
*
- * <table border=1 summary="Shows how FormatType and FormatStyle values map to Format instances">
+ * <table class="plain">
+ * <caption style="display:none">Shows how FormatType and FormatStyle values map to Format instances</caption>
+ * <thead>
* <tr>
- * <th id="ft" class="TableHeadingColor">FormatType
- * <th id="fs" class="TableHeadingColor">FormatStyle
- * <th id="sc" class="TableHeadingColor">Subformat Created
+ * <th scope="col" class="TableHeadingColor">FormatType
+ * <th scope="col" class="TableHeadingColor">FormatStyle
+ * <th scope="col" class="TableHeadingColor">Subformat Created
+ * </thead>
+ * <tbody>
* <tr>
- * <td headers="ft"><i>(none)</i>
- * <td headers="fs"><i>(none)</i>
- * <td headers="sc"><code>null</code>
+ * <th scope="row" style="text-weight: normal"><i>(none)</i>
+ * <th scope="row" style="text-weight: normal"><i>(none)</i>
+ * <td>{@code null}
* <tr>
- * <td headers="ft" rowspan=5><code>number</code>
- * <td headers="fs"><i>(none)</i>
- * <td headers="sc">{@link NumberFormat#getInstance(Locale) NumberFormat.getInstance}{@code (getLocale())}
+ * <th scope="row" style="text-weight: normal" rowspan=5>{@code number}
+ * <th scope="row" style="text-weight: normal"><i>(none)</i>
+ * <td>{@link NumberFormat#getInstance(Locale) NumberFormat.getInstance}{@code (getLocale())}
* <tr>
- * <td headers="fs"><code>integer</code>
- * <td headers="sc">{@link NumberFormat#getIntegerInstance(Locale) NumberFormat.getIntegerInstance}{@code (getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code integer}
+ * <td>{@link NumberFormat#getIntegerInstance(Locale) NumberFormat.getIntegerInstance}{@code (getLocale())}
* <tr>
- * <td headers="fs"><code>currency</code>
- * <td headers="sc">{@link NumberFormat#getCurrencyInstance(Locale) NumberFormat.getCurrencyInstance}{@code (getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code currency}
+ * <td>{@link NumberFormat#getCurrencyInstance(Locale) NumberFormat.getCurrencyInstance}{@code (getLocale())}
* <tr>
- * <td headers="fs"><code>percent</code>
- * <td headers="sc">{@link NumberFormat#getPercentInstance(Locale) NumberFormat.getPercentInstance}{@code (getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code percent}
+ * <td>{@link NumberFormat#getPercentInstance(Locale) NumberFormat.getPercentInstance}{@code (getLocale())}
* <tr>
- * <td headers="fs"><i>SubformatPattern</i>
- * <td headers="sc">{@code new} {@link DecimalFormat#DecimalFormat(String,DecimalFormatSymbols) DecimalFormat}{@code (subformatPattern,} {@link DecimalFormatSymbols#getInstance(Locale) DecimalFormatSymbols.getInstance}{@code (getLocale()))}
+ * <th scope="row" style="text-weight: normal"><i>SubformatPattern</i>
+ * <td>{@code new} {@link DecimalFormat#DecimalFormat(String,DecimalFormatSymbols) DecimalFormat}{@code (subformatPattern,} {@link DecimalFormatSymbols#getInstance(Locale) DecimalFormatSymbols.getInstance}{@code (getLocale()))}
* <tr>
- * <td headers="ft" rowspan=6><code>date</code>
- * <td headers="fs"><i>(none)</i>
- * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal" rowspan=6>{@code date}
+ * <th scope="row" style="text-weight: normal"><i>(none)</i>
+ * <td>{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
* <tr>
- * <td headers="fs"><code>short</code>
- * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code short}
+ * <td>{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())}
* <tr>
- * <td headers="fs"><code>medium</code>
- * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code medium}
+ * <td>{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
* <tr>
- * <td headers="fs"><code>long</code>
- * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code long}
+ * <td>{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())}
* <tr>
- * <td headers="fs"><code>full</code>
- * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code full}
+ * <td>{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())}
* <tr>
- * <td headers="fs"><i>SubformatPattern</i>
- * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())}
+ * <th scope="row" style="text-weight: normal"><i>SubformatPattern</i>
+ * <td>{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())}
* <tr>
- * <td headers="ft" rowspan=6><code>time</code>
- * <td headers="fs"><i>(none)</i>
- * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal" rowspan=6>{@code time}
+ * <th scope="row" style="text-weight: normal"><i>(none)</i>
+ * <td>{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
* <tr>
- * <td headers="fs"><code>short</code>
- * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code short}
+ * <td>{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())}
* <tr>
- * <td headers="fs"><code>medium</code>
- * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code medium}
+ * <td>{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
* <tr>
- * <td headers="fs"><code>long</code>
- * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code long}
+ * <td>{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())}
* <tr>
- * <td headers="fs"><code>full</code>
- * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())}
+ * <th scope="row" style="text-weight: normal">{@code full}
+ * <td>{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())}
* <tr>
- * <td headers="fs"><i>SubformatPattern</i>
- * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())}
+ * <th scope="row" style="text-weight: normal"><i>SubformatPattern</i>
+ * <td>{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())}
* <tr>
- * <td headers="ft"><code>choice</code>
- * <td headers="fs"><i>SubformatPattern</i>
- * <td headers="sc">{@code new} {@link ChoiceFormat#ChoiceFormat(String) ChoiceFormat}{@code (subformatPattern)}
+ * <th scope="row" style="text-weight: normal">{@code choice}
+ * <th scope="row" style="text-weight: normal"><i>SubformatPattern</i>
+ * <td>{@code new} {@link ChoiceFormat#ChoiceFormat(String) ChoiceFormat}{@code (subformatPattern)}
+ * </tbody>
* </table>
*
* <h4>Usage Information</h4>
@@ -322,7 +327,7 @@
* // result now equals {new String("z")}
* </pre></blockquote>
*
- * <h4><a name="synchronization">Synchronization</a></h4>
+ * <h4><a id="synchronization">Synchronization</a></h4>
*
* <p>
* Message formats are not synchronized.
@@ -340,6 +345,7 @@
* @see SimpleDateFormat
*
* @author Mark Davis
+ * @since 1.1
*/
public class MessageFormat extends Format {
@@ -357,6 +363,8 @@
*
* @param pattern the pattern for this message format
* @exception IllegalArgumentException if the pattern is invalid
+ * @exception NullPointerException if {@code pattern} is
+ * {@code null}
*/
public MessageFormat(String pattern) {
this.locale = Locale.getDefault(Locale.Category.FORMAT);
@@ -374,6 +382,8 @@
* @param pattern the pattern for this message format
* @param locale the locale for this message format
* @exception IllegalArgumentException if the pattern is invalid
+ * @exception NullPointerException if {@code pattern} is
+ * {@code null}
* @since 1.4
*/
public MessageFormat(String pattern, Locale locale) {
@@ -421,6 +431,8 @@
*
* @param pattern the pattern for this message format
* @exception IllegalArgumentException if the pattern is invalid
+ * @exception NullPointerException if {@code pattern} is
+ * {@code null}
*/
@SuppressWarnings("fallthrough") // fallthrough in switch is expected, suppress it
public void applyPattern(String pattern) {
@@ -690,11 +702,8 @@
* larger than the number of format elements in the pattern string
*/
public void setFormat(int formatElementIndex, Format newFormat) {
- // Android-added: prevent setting unused formatters.
if (formatElementIndex > maxOffset) {
- // Note: maxOffset is maximum index, not the length.
- throw new ArrayIndexOutOfBoundsException(
- "maxOffset=" + maxOffset + "; formatElementIndex=" + formatElementIndex);
+ throw new ArrayIndexOutOfBoundsException(formatElementIndex);
}
formats[formatElementIndex] = newFormat;
}
@@ -767,45 +776,46 @@
* argument is <i>unavailable</i> if <code>arguments</code> is
* <code>null</code> or has fewer than argumentIndex+1 elements.
*
- * <table border=1 summary="Examples of subformat,argument,and formatted text">
+ * <table class="plain">
+ * <caption style="display:none">Examples of subformat,argument,and formatted text</caption>
+ * <thead>
* <tr>
- * <th>Subformat
- * <th>Argument
- * <th>Formatted Text
+ * <th scope="col">Subformat
+ * <th scope="col">Argument
+ * <th scope="col">Formatted Text
+ * </thead>
+ * <tbody>
* <tr>
- * <td><i>any</i>
- * <td><i>unavailable</i>
+ * <th scope="row" style="text-weight-normal" rowspan=2><i>any</i>
+ * <th scope="row" style="text-weight-normal"><i>unavailable</i>
* <td><code>"{" + argumentIndex + "}"</code>
* <tr>
- * <td><i>any</i>
- * <td><code>null</code>
+ * <th scope="row" style="text-weight-normal"><code>null</code>
* <td><code>"null"</code>
* <tr>
- * <td><code>instanceof ChoiceFormat</code>
- * <td><i>any</i>
+ * <th scope="row" style="text-weight-normal"><code>instanceof ChoiceFormat</code>
+ * <th scope="row" style="text-weight-normal"><i>any</i>
* <td><code>subformat.format(argument).indexOf('{') >= 0 ?<br>
* (new MessageFormat(subformat.format(argument), getLocale())).format(argument) :
* subformat.format(argument)</code>
* <tr>
- * <td><code>!= null</code>
- * <td><i>any</i>
+ * <th scope="row" style="text-weight-normal"><code>!= null</code>
+ * <th scope="row" style="text-weight-normal"><i>any</i>
* <td><code>subformat.format(argument)</code>
* <tr>
- * <td><code>null</code>
- * <td><code>instanceof Number</code>
+ * <th scope="row" style="text-weight-normal" rowspan=4><code>null</code>
+ * <th scope="row" style="text-weight-normal"><code>instanceof Number</code>
* <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code>
* <tr>
- * <td><code>null</code>
- * <td><code>instanceof Date</code>
+ * <th scope="row" style="text-weight-normal"><code>instanceof Date</code>
* <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getLocale()).format(argument)</code>
* <tr>
- * <td><code>null</code>
- * <td><code>instanceof String</code>
+ * <th scope="row" style="text-weight-normal"><code>instanceof String</code>
* <td><code>argument</code>
* <tr>
- * <td><code>null</code>
- * <td><i>any</i>
+ * <th scope="row" style="text-weight-normal"><i>any</i>
* <td><code>argument.toString()</code>
+ * </tbody>
* </table>
* <p>
* If <code>pos</code> is non-null, and refers to
@@ -814,13 +824,14 @@
*
* @param arguments an array of objects to be formatted and substituted.
* @param result where text is appended.
- * @param pos On input: an alignment field, if desired.
- * On output: the offsets of the alignment field.
+ * @param pos keeps track on the position of the first replaced argument
+ in the output string.
* @return the string buffer passed in as {@code result}, with formatted
* text appended
* @exception IllegalArgumentException if an argument in the
* <code>arguments</code> array is not of the type
* expected by the format element(s) that use it.
+ * @exception NullPointerException if {@code result} is {@code null}
*/
public final StringBuffer format(Object[] arguments, StringBuffer result,
FieldPosition pos)
@@ -842,6 +853,7 @@
* or if an argument in the <code>arguments</code> array
* is not of the type expected by the format element(s)
* that use it.
+ * @exception NullPointerException if {@code pattern} is {@code null}
*/
public static String format(String pattern, Object ... arguments) {
MessageFormat temp = new MessageFormat(pattern);
@@ -860,11 +872,12 @@
*
* @param arguments an array of objects to be formatted and substituted.
* @param result where text is appended.
- * @param pos On input: an alignment field, if desired.
- * On output: the offsets of the alignment field.
+ * @param pos keeps track on the position of the first replaced argument
+ * in the output string.
* @exception IllegalArgumentException if an argument in the
* <code>arguments</code> array is not of the type
* expected by the format element(s) that use it.
+ * @exception NullPointerException if {@code result} is {@code null}
*/
public final StringBuffer format(Object arguments, StringBuffer result,
FieldPosition pos)
@@ -954,6 +967,8 @@
* @param source the string to parse
* @param pos the parse position
* @return an array of parsed objects
+ * @exception NullPointerException if {@code pos} is {@code null}
+ * for a non-null {@code source} string.
*/
public Object[] parse(String source, ParsePosition pos) {
if (source == null) {
@@ -1075,7 +1090,7 @@
* index information as described above.
* @return An <code>Object</code> array parsed from the string. In case of
* error, returns null.
- * @exception NullPointerException if <code>pos</code> is null.
+ * @throws NullPointerException if {@code pos} is null.
*/
public Object parseObject(String source, ParsePosition pos) {
return parse(source, pos);
@@ -1175,7 +1190,7 @@
* indicating the index in the <code>arguments</code> array of the
* argument from which the text was generated.
*/
- public final static Field ARGUMENT =
+ public static final Field ARGUMENT =
new Field("message argument field");
}
@@ -1228,11 +1243,11 @@
private int maxOffset = -1;
/**
- * Internal routine used by format. If <code>characterIterators</code> is
- * non-null, AttributedCharacterIterator will be created from the
- * subformats as necessary. If <code>characterIterators</code> is null
- * and <code>fp</code> is non-null and identifies
- * <code>Field.MESSAGE_ARGUMENT</code>, the location of
+ * Internal routine used by format. If {@code characterIterators} is
+ * {@code non-null}, AttributedCharacterIterator will be created from the
+ * subformats as necessary. If {@code characterIterators} is {@code null}
+ * and {@code fp} is {@code non-null} and identifies
+ * {@code Field.ARGUMENT} as the field attribute, the location of
* the first replaced argument will be set in it.
*
* @exception IllegalArgumentException if an argument in the
@@ -1246,7 +1261,7 @@
int lastOffset = 0;
int last = result.length();
for (int i = 0; i <= maxOffset; ++i) {
- result.append(pattern.substring(lastOffset, offsets[i]));
+ result.append(pattern, lastOffset, offsets[i]);
lastOffset = offsets[i];
int argumentNumber = argumentNumbers[i];
if (arguments == null || argumentNumber >= arguments.length) {
@@ -1315,7 +1330,7 @@
}
arg = null;
}
- if (arg != null && arg.length() > 0) {
+ if (arg != null && !arg.isEmpty()) {
result.append(arg);
characterIterators.add(
createAttributedCharacterIterator(
@@ -1339,7 +1354,7 @@
}
}
}
- result.append(pattern.substring(lastOffset, pattern.length()));
+ result.append(pattern, lastOffset, pattern.length());
if (characterIterators != null && last != result.length()) {
characterIterators.add(createAttributedCharacterIterator(
result.substring(last)));
@@ -1461,7 +1476,7 @@
// now get the format
Format newFormat = null;
- if (segments[SEG_TYPE].length() != 0) {
+ if (!segments[SEG_TYPE].isEmpty()) {
int type = findKeyword(segments[SEG_TYPE], TYPE_KEYWORDS);
switch (type) {
case TYPE_NULL:
diff --git a/ojluni/src/main/java/java/text/NumberFormat.java b/ojluni/src/main/java/java/text/NumberFormat.java
index 9ad44ed..5007b04 100644
--- a/ojluni/src/main/java/java/text/NumberFormat.java
+++ b/ojluni/src/main/java/java/text/NumberFormat.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -55,6 +55,9 @@
import libcore.icu.ICU;
import libcore.icu.LocaleData;
+// Android-removed: Remove javadoc related to "rg" Locale extension.
+// The "rg" extension isn't supported until https://unicode-org.atlassian.net/browse/ICU-21831
+// is resolved, because java.text.* stack relies on ICU on resource resolution.
/**
* <code>NumberFormat</code> is the abstract base class for all number
* formats. This class provides the interface for formatting and parsing
@@ -94,7 +97,12 @@
* NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH);
* }</pre>
* </blockquote>
- * You can also use a <code>NumberFormat</code> to parse numbers:
+ *
+ * <p>If the locale contains "nu" (numbers)
+ * <a href="../util/Locale.html#def_locale_extension">Unicode extensions</a>,
+ * the decimal digits, and/or the country used for formatting are overridden.
+ *
+ * <p>You can also use a {@code NumberFormat} to parse numbers:
* <blockquote>
* <pre>{@code
* myNumber = nf.parse(myString);
@@ -164,7 +172,7 @@
* numbers: "(12)" for -12.
* </ol>
*
- * <h3><a name="synchronization">Synchronization</a></h3>
+ * <h3><a id="synchronization">Synchronization</a></h3>
*
* <p>
* Number formats are generally not synchronized.
@@ -172,10 +180,29 @@
* If multiple threads access a format concurrently, it must be synchronized
* externally.
*
+ * @implSpec The {@link #format(double, StringBuffer, FieldPosition)},
+ * {@link #format(long, StringBuffer, FieldPosition)} and
+ * {@link #parse(String, ParsePosition)} methods may throw
+ * {@code NullPointerException}, if any of their parameter is {@code null}.
+ * The subclass may provide its own implementation and specification about
+ * {@code NullPointerException}.
+ *
+ * <p>
+ * The default implementation provides rounding modes defined
+ * in {@link java.math.RoundingMode} for formatting numbers. It
+ * uses the {@linkplain java.math.RoundingMode#HALF_EVEN
+ * round half-even algorithm}. To change the rounding mode use
+ * {@link #setRoundingMode(java.math.RoundingMode) setRoundingMode}.
+ * The {@code NumberFormat} returned by the static factory methods is
+ * configured to round floating point numbers using half-even
+ * rounding (see {@link java.math.RoundingMode#HALF_EVEN
+ * RoundingMode.HALF_EVEN}) for formatting.
+ *
* @see DecimalFormat
* @see ChoiceFormat
* @author Mark Davis
* @author Helena Shih
+ * @since 1.1
*/
public abstract class NumberFormat extends Format {
@@ -219,8 +246,14 @@
* @param number the number to format
* @param toAppendTo the <code>StringBuffer</code> to which the formatted
* text is to be appended
- * @param pos On input: an alignment field, if desired.
- * On output: the offsets of the alignment field.
+ * @param pos keeps track on the position of the field within the
+ * returned string. For example, for formatting a number
+ * {@code 1234567.89} in {@code Locale.US} locale,
+ * if the given {@code fieldPosition} is
+ * {@link NumberFormat#INTEGER_FIELD}, the begin index
+ * and end index of {@code fieldPosition} will be set
+ * to 0 and 9, respectively for the output string
+ * {@code 1,234,567.89}.
* @return the value passed in as <code>toAppendTo</code>
* @exception IllegalArgumentException if <code>number</code> is
* null or not an instance of <code>Number</code>.
@@ -269,7 +302,7 @@
* index information as described above.
* @return A <code>Number</code> parsed from the string. In case of
* error, returns null.
- * @exception NullPointerException if <code>pos</code> is null.
+ * @throws NullPointerException if {@code source} or {@code pos} is null.
*/
@Override
public final Object parseObject(String source, ParsePosition pos) {
@@ -313,7 +346,14 @@
* @param number the double number to format
* @param toAppendTo the StringBuffer to which the formatted text is to be
* appended
- * @param pos the field position
+ * @param pos keeps track on the position of the field within the
+ * returned string. For example, for formatting a number
+ * {@code 1234567.89} in {@code Locale.US} locale,
+ * if the given {@code fieldPosition} is
+ * {@link NumberFormat#INTEGER_FIELD}, the begin index
+ * and end index of {@code fieldPosition} will be set
+ * to 0 and 9, respectively for the output string
+ * {@code 1,234,567.89}.
* @return the formatted StringBuffer
* @exception ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
@@ -329,7 +369,14 @@
* @param number the long number to format
* @param toAppendTo the StringBuffer to which the formatted text is to be
* appended
- * @param pos the field position
+ * @param pos keeps track on the position of the field within the
+ * returned string. For example, for formatting a number
+ * {@code 123456789} in {@code Locale.US} locale,
+ * if the given {@code fieldPosition} is
+ * {@link NumberFormat#INTEGER_FIELD}, the begin index
+ * and end index of {@code fieldPosition} will be set
+ * to 0 and 11, respectively for the output string
+ * {@code 123,456,789}.
* @return the formatted StringBuffer
* @exception ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
@@ -383,7 +430,7 @@
* For example in the English locale, with ParseIntegerOnly true, the
* string "1234." would be parsed as the integer value 1234 and parsing
* would stop at the "." character. Of course, the exact format accepted
- * by the parse operation is locale dependant and determined by sub-classes
+ * by the parse operation is locale dependent and determined by sub-classes
* of NumberFormat.
*
* @return {@code true} if numbers should be parsed as integers only;
@@ -415,7 +462,7 @@
* @return the {@code NumberFormat} instance for general-purpose number
* formatting
*/
- public final static NumberFormat getInstance() {
+ public static final NumberFormat getInstance() {
return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
}
@@ -444,7 +491,7 @@
* @see java.util.Locale#getDefault(java.util.Locale.Category)
* @see java.util.Locale.Category#FORMAT
*/
- public final static NumberFormat getNumberInstance() {
+ public static final NumberFormat getNumberInstance() {
return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
}
@@ -477,7 +524,7 @@
* @return a number format for integer values
* @since 1.4
*/
- public final static NumberFormat getIntegerInstance() {
+ public static final NumberFormat getIntegerInstance() {
return getInstance(Locale.getDefault(Locale.Category.FORMAT), INTEGERSTYLE);
}
@@ -509,7 +556,7 @@
* @see java.util.Locale#getDefault(java.util.Locale.Category)
* @see java.util.Locale.Category#FORMAT
*/
- public final static NumberFormat getCurrencyInstance() {
+ public static final NumberFormat getCurrencyInstance() {
return getInstance(Locale.getDefault(Locale.Category.FORMAT), CURRENCYSTYLE);
}
@@ -534,7 +581,7 @@
* @see java.util.Locale#getDefault(java.util.Locale.Category)
* @see java.util.Locale.Category#FORMAT
*/
- public final static NumberFormat getPercentInstance() {
+ public static final NumberFormat getPercentInstance() {
return getInstance(Locale.getDefault(Locale.Category.FORMAT), PERCENTSTYLE);
}
@@ -609,7 +656,7 @@
* Returns true if grouping is used in this format. For example, in the
* English locale, with grouping on, the number 1234567 might be formatted
* as "1,234,567". The grouping separator as well as the size of each group
- * is locale dependant and is determined by sub-classes of NumberFormat.
+ * is locale dependent and is determined by sub-classes of NumberFormat.
*
* @return {@code true} if grouping is used;
* {@code false} otherwise
diff --git a/ojluni/src/main/java/java/text/SimpleDateFormat.java b/ojluni/src/main/java/java/text/SimpleDateFormat.java
index 36a4601..53a8dbe 100644
--- a/ojluni/src/main/java/java/text/SimpleDateFormat.java
+++ b/ojluni/src/main/java/java/text/SimpleDateFormat.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -104,168 +104,173 @@
* <code>'A'</code> to <code>'Z'</code> and from <code>'a'</code> to
* <code>'z'</code> are reserved):
* <blockquote>
- * <table border=0 cellspacing=3 cellpadding=0 summary="Chart shows pattern letters, date/time component, presentation, and examples.">
- * <tr style="background-color: rgb(204, 204, 255);">
- * <th align=left>Letter
- * <th align=left>Date or Time Component
- * <th align=left>Presentation
- * <th align=left>Examples
- * <th align=left>Supported (API Levels)
+ * <table class="striped">
+ * <caption style="display:none">Chart shows pattern letters, date/time component, presentation, and examples.</caption>
+ * <thead>
* <tr>
- * <td><code>G</code>
+ * <th scope="col" style="text-align:left">Letter
+ * <th scope="col" style="text-align:left">Date or Time Component
+ * <th scope="col" style="text-align:left">Presentation
+ * <th scope="col" style="text-align:left">Examples
+ * <th scope="col" style="text-align:left">Supported (API Levels)
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th scope="row"><code>G</code>
* <td>Era designator
* <td><a href="#text">Text</a>
* <td><code>AD</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>y</code>
+ * <tr>
+ * <th scope="row"><code>y</code>
* <td>Year
* <td><a href="#year">Year</a>
* <td><code>1996</code>; <code>96</code>
* <td>1+</td>
* <tr>
- * <td><code>Y</code>
+ * <th scope="row"><code>Y</code>
* <td>Week year
* <td><a href="#year">Year</a>
* <td><code>2009</code>; <code>09</code>
* <td>24+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>M</code>
+ * <tr>
+ * <th scope="row"><code>M</code>
* <td>Month in year (context sensitive)
* <td><a href="#month">Month</a>
* <td><code>July</code>; <code>Jul</code>; <code>07</code>
* <td>1+</td>
* <tr>
- * <td><code>L</code>
+ * <th scope="row"><code>L</code>
* <td>Month in year (standalone form)
* <td><a href="#month">Month</a>
* <td><code>July</code>; <code>Jul</code>; <code>07</code>
* <td>TBD</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>w</code>
+ * <tr>
+ * <th scope="row"><code>w</code>
* <td>Week in year
* <td><a href="#number">Number</a>
* <td><code>27</code>
* <td>1+</td>
* <tr>
- * <td><code>W</code>
+ * <th scope="row"><code>W</code>
* <td>Week in month
* <td><a href="#number">Number</a>
* <td><code>2</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>D</code>
+ * <tr>
+ * <th scope="row"><code>D</code>
* <td>Day in year
* <td><a href="#number">Number</a>
* <td><code>189</code>
* <td>1+</td>
* <tr>
- * <td><code>d</code>
+ * <th scope="row"><code>d</code>
* <td>Day in month
* <td><a href="#number">Number</a>
* <td><code>10</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>F</code>
+ * <tr>
+ * <th scope="row"><code>F</code>
* <td>Day of week in month
* <td><a href="#number">Number</a>
* <td><code>2</code>
* <td>1+</td>
* <tr>
- * <td><code>E</code>
+ * <th scope="row"><code>E</code>
* <td>Day name in week
* <td><a href="#text">Text</a>
* <td><code>Tuesday</code>; <code>Tue</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>u</code>
+ * <tr>
+ * <th scope="row"><code>u</code>
* <td>Day number of week (1 = Monday, ..., 7 = Sunday)
* <td><a href="#number">Number</a>
* <td><code>1</code>
* <td>24+</td>
* <tr>
- * <td><code>a</code>
+ * <th scope="row"><code>a</code>
* <td>Am/pm marker
* <td><a href="#text">Text</a>
* <td><code>PM</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>H</code>
+ * <tr>
+ * <th scope="row"><code>H</code>
* <td>Hour in day (0-23)
* <td><a href="#number">Number</a>
* <td><code>0</code>
* <td>1+</td>
* <tr>
- * <td><code>k</code>
+ * <th scope="row"><code>k</code>
* <td>Hour in day (1-24)
* <td><a href="#number">Number</a>
* <td><code>24</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>K</code>
+ * <tr>
+ * <th scope="row"><code>K</code>
* <td>Hour in am/pm (0-11)
* <td><a href="#number">Number</a>
* <td><code>0</code>
* <td>1+</td>
* <tr>
- * <td><code>h</code>
+ * <th scope="row"><code>h</code>
* <td>Hour in am/pm (1-12)
* <td><a href="#number">Number</a>
* <td><code>12</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>m</code>
+ * <tr>
+ * <th scope="row"><code>m</code>
* <td>Minute in hour
* <td><a href="#number">Number</a>
* <td><code>30</code>
* <td>1+</td>
* <tr>
- * <td><code>s</code>
+ * <th scope="row"><code>s</code>
* <td>Second in minute
* <td><a href="#number">Number</a>
* <td><code>55</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>S</code>
+ * <tr>
+ * <th scope="row"><code>S</code>
* <td>Millisecond
* <td><a href="#number">Number</a>
* <td><code>978</code>
* <td>1+</td>
* <tr>
- * <td><code>z</code>
+ * <th scope="row"><code>z</code>
* <td>Time zone
* <td><a href="#timezone">General time zone</a>
* <td><code>Pacific Standard Time</code>; <code>PST</code>; <code>GMT-08:00</code>
* <td>1+</td>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>Z</code>
+ * <tr>
+ * <th scope="row"><code>Z</code>
* <td>Time zone
* <td><a href="#rfc822timezone">RFC 822 time zone</a>
* <td><code>-0800</code>
* <td>1+</td>
* <tr>
- * <td><code>X</code>
+ * <th scope="row"><code>X</code>
* <td>Time zone
* <td><a href="#iso8601timezone">ISO 8601 time zone</a>
* <td><code>-08</code>; <code>-0800</code>; <code>-08:00</code>
* <td>24+</td>
+ * </tbody>
* </table>
* </blockquote>
* Pattern letters are usually repeated, as their number determines the
* exact presentation:
* <ul>
- * <li><strong><a name="text">Text:</a></strong>
+ * <li><strong><a id="text">Text:</a></strong>
* For formatting, if the number of pattern letters is 4 or more,
* the full form is used; otherwise a short or abbreviated form
* is used if available.
* For parsing, both forms are accepted, independent of the number
* of pattern letters.</li>
- * <li><strong><a name="number">Number:</a></strong>
+ * <li><strong><a id="number">Number:</a></strong>
* For formatting, the number of pattern letters is the minimum
* number of digits, and shorter numbers are zero-padded to this amount.
* For parsing, the number of pattern letters is ignored unless
* it's needed to separate two adjacent fields.</li>
- * <li><strong><a name="year">Year:</a></strong>
+ * <li><strong><a id="year">Year:</a></strong>
* If the formatter's {@link #getCalendar() Calendar} is the Gregorian
* calendar, the following rules are applied.
* <ul>
@@ -306,26 +311,33 @@
* DateFormat#getCalendar() getCalendar()}.{@link
* java.util.Calendar#isWeekDateSupported()
* isWeekDateSupported()}.</li>
- * <li><strong><a name="month">Month:</a></strong>
+ * <li><strong><a id="month">Month:</a></strong>
* If the number of pattern letters is 3 or more, the month is
* interpreted as <a href="#text">text</a>; otherwise,
* it is interpreted as a <a href="#number">number</a>.
* <ul>
* <li>Letter <em>M</em> produces context-sensitive month names, such as the
- * embedded form of names. If a {@code DateFormatSymbols} has been set
- * explicitly with constructor {@link #SimpleDateFormat(String,
- * DateFormatSymbols)} or method {@link
+ * embedded form of names. Letter <em>M</em> is context-sensitive in the
+ * sense that when it is used in the standalone pattern, for example,
+ * "MMMM", it gives the standalone form of a month name and when it is
+ * used in the pattern containing other field(s), for example, "d MMMM",
+ * it gives the format form of a month name. For example, January in the
+ * Catalan language is "de gener" in the format form while it is "gener"
+ * in the standalone form. In this case, "MMMM" will produce "gener" and
+ * the month part of the "d MMMM" will produce "de gener". If a
+ * {@code DateFormatSymbols} has been set explicitly with constructor
+ * {@link #SimpleDateFormat(String,DateFormatSymbols)} or method {@link
* #setDateFormatSymbols(DateFormatSymbols)}, the month names given by
* the {@code DateFormatSymbols} are used.</li>
* <li>Letter <em>L</em> produces the standalone form of month names.</li>
* </ul>
* <br></li>
- * <li><strong><a name="timezone">General time zone:</a></strong>
+ * <li><strong><a id="timezone">General time zone:</a></strong>
* Time zones are interpreted as <a href="#text">text</a> if they have
* names. For time zones representing a GMT offset value, the
* following syntax is used:
* <pre>
- * <a name="GMTOffsetTimeZone"><i>GMTOffsetTimeZone:</i></a>
+ * <a id="GMTOffsetTimeZone"><i>GMTOffsetTimeZone:</i></a>
* <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i>
* <i>Sign:</i> one of
* <code>+ -</code>
@@ -341,7 +353,7 @@
* from the Basic Latin block of the Unicode standard.
* <p>For parsing, <a href="#rfc822timezone">RFC 822 time zones</a> are also
* accepted.</li>
- * <li><strong><a name="rfc822timezone">RFC 822 time zone:</a></strong>
+ * <li><strong><a id="rfc822timezone">RFC 822 time zone:</a></strong>
* For formatting, the RFC 822 4-digit time zone format is used:
*
* <pre>
@@ -354,7 +366,7 @@
*
* <p>For parsing, <a href="#timezone">general time zones</a> are also
* accepted.
- * <li><strong><a name="iso8601timezone">ISO 8601 Time zone:</a></strong>
+ * <li><strong><a id="iso8601timezone">ISO 8601 Time zone:</a></strong>
* The number of pattern letters designates the format for both formatting
* and parsing as follows:
* <pre>
@@ -399,47 +411,52 @@
* the U.S. locale. The given date and time are 2001-07-04 12:08:56 local time
* in the U.S. Pacific Time time zone.
* <blockquote>
- * <table border=0 cellspacing=3 cellpadding=0 summary="Examples of date and time patterns interpreted in the U.S. locale">
- * <tr style="background-color: rgb(204, 204, 255);">
- * <th align=left>Date and Time Pattern
- * <th align=left>Result
+ * <table class="striped">
+ * <caption style="display:none">Examples of date and time patterns interpreted in the U.S. locale</caption>
+ * <thead>
* <tr>
- * <td><code>"yyyy.MM.dd G 'at' HH:mm:ss z"</code>
+ * <th scope="col" style="text-align:left">Date and Time Pattern
+ * <th scope="col" style="text-align:left">Result
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th scope="row"><code>"yyyy.MM.dd G 'at' HH:mm:ss z"</code>
* <td><code>2001.07.04 AD at 12:08:56 PDT</code>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>"EEE, MMM d, ''yy"</code>
+ * <tr>
+ * <th scope="row"><code>"EEE, MMM d, ''yy"</code>
* <td><code>Wed, Jul 4, '01</code>
* <tr>
- * <td><code>"h:mm a"</code>
+ * <th scope="row"><code>"h:mm a"</code>
* <td><code>12:08 PM</code>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>"hh 'o''clock' a, zzzz"</code>
+ * <tr>
+ * <th scope="row"><code>"hh 'o''clock' a, zzzz"</code>
* <td><code>12 o'clock PM, Pacific Daylight Time</code>
* <tr>
- * <td><code>"K:mm a, z"</code>
+ * <th scope="row"><code>"K:mm a, z"</code>
* <td><code>0:08 PM, PDT</code>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>"yyyyy.MMMM.dd GGG hh:mm aaa"</code>
+ * <tr>
+ * <th scope="row"><code>"yyyyy.MMMM.dd GGG hh:mm aaa"</code>
* <td><code>02001.July.04 AD 12:08 PM</code>
* <tr>
- * <td><code>"EEE, d MMM yyyy HH:mm:ss Z"</code>
+ * <th scope="row"><code>"EEE, d MMM yyyy HH:mm:ss Z"</code>
* <td><code>Wed, 4 Jul 2001 12:08:56 -0700</code>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>"yyMMddHHmmssZ"</code>
+ * <tr>
+ * <th scope="row"><code>"yyMMddHHmmssZ"</code>
* <td><code>010704120856-0700</code>
* <tr>
- * <td><code>"yyyy-MM-dd'T'HH:mm:ss.SSSZ"</code>
+ * <th scope="row"><code>"yyyy-MM-dd'T'HH:mm:ss.SSSZ"</code>
* <td><code>2001-07-04T12:08:56.235-0700</code>
- * <tr style="background-color: rgb(238, 238, 255);">
- * <td><code>"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"</code>
+ * <tr>
+ * <th scope="row"><code>"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"</code>
* <td><code>2001-07-04T12:08:56.235-07:00</code>
* <tr>
- * <td><code>"YYYY-'W'ww-u"</code>
+ * <th scope="row"><code>"YYYY-'W'ww-u"</code>
* <td><code>2001-W27-3</code>
+ * </tbody>
* </table>
* </blockquote>
*
- * <h4><a name="synchronization">Synchronization</a></h4>
+ * <h4><a id="synchronization">Synchronization</a></h4>
*
* <p>
* Date formats are not synchronized.
@@ -447,12 +464,13 @@
* If multiple threads access a format concurrently, it must be synchronized
* externally.
*
- * @see <a href="https://docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html">Java Tutorial</a>
+ * @see <a href="http://docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html">Java Tutorial</a>
* @see java.util.Calendar
* @see java.util.TimeZone
* @see DateFormat
* @see DateFormatSymbols
* @author Mark Davis, Chen-Lieh Huang, Alan Liu
+ * @since 1.1
*/
public class SimpleDateFormat extends DateFormat {
@@ -477,7 +495,7 @@
* and the highest allowable <code>serialVersionOnStream</code>
* is written.
* @serial
- * @since JDK1.1.4
+ * @since 1.1.4
*/
private int serialVersionOnStream = currentSerialVersion;
@@ -492,19 +510,19 @@
* Saved numberFormat and pattern.
* @see SimpleDateFormat#checkNegativeNumberExpression
*/
- transient private NumberFormat originalNumberFormat;
- transient private String originalNumberPattern;
+ private transient NumberFormat originalNumberFormat;
+ private transient String originalNumberPattern;
/**
* The minus sign to be used with format and parse.
*/
- transient private char minusSign = '-';
+ private transient char minusSign = '-';
/**
* True when a negative sign follows a number.
* (True as default in Arabic.)
*/
- transient private boolean hasFollowingMinusSign = false;
+ private transient boolean hasFollowingMinusSign = false;
// BEGIN Android-removed: App compat for formatting pattern letter M.
// OpenJDK forces the standalone form of month when patterns contain pattern M only.
@@ -514,27 +532,27 @@
/**
* True if standalone form needs to be used.
*
- transient private boolean forceStandaloneForm = false;
+ private transient boolean forceStandaloneForm = false;
*/
// END Android-removed: App compat for formatting pattern letter M.
/**
* The compiled pattern.
*/
- transient private char[] compiledPattern;
+ private transient char[] compiledPattern;
/**
* Tags for the compiled pattern.
*/
- private final static int TAG_QUOTE_ASCII_CHAR = 100;
- private final static int TAG_QUOTE_CHARS = 101;
+ private static final int TAG_QUOTE_ASCII_CHAR = 100;
+ private static final int TAG_QUOTE_CHARS = 101;
/**
* Locale dependent digit zero.
* @see #zeroPaddingNumber
* @see java.text.DecimalFormatSymbols#getZeroDigit
*/
- transient private char zeroDigit;
+ private transient char zeroDigit;
/**
* The symbols used by this formatter for week names, month names,
@@ -549,11 +567,11 @@
* <code>defaultCenturyStart</code>, which may be any date. May
* not be null.
* @serial
- * @since JDK1.1.4
+ * @since 1.1.4
*/
private Date defaultCenturyStart;
- transient private int defaultCenturyStartYear;
+ private transient int defaultCenturyStartYear;
private static final int MILLIS_PER_MINUTE = 60 * 1000;
@@ -737,7 +755,7 @@
// However, the calendar should use the current default TimeZone.
// If this is not contained in the locale zone strings, then the zone
// will be formatted using generic GMT+/-H:MM nomenclature.
- calendar = Calendar.getInstance(TimeZone.getDefault(), loc);
+ calendar = Calendar.getInstance(loc);
}
}
@@ -1015,6 +1033,7 @@
* @param startDate During parsing, two digit years will be placed in the range
* <code>startDate</code> to <code>startDate + 100 years</code>.
* @see #get2DigitYearStart
+ * @throws NullPointerException if {@code startDate} is {@code null}.
* @since 1.2
*/
public void set2DigitYearStart(Date startDate) {
@@ -1040,10 +1059,21 @@
*
* @param date the date-time value to be formatted into a date-time string.
* @param toAppendTo where the new date-time text is to be appended.
- * @param pos the formatting position. On input: an alignment field,
- * if desired. On output: the offsets of the alignment field.
+ * @param pos keeps track on the position of the field within
+ * the returned string. For example, given a date-time text
+ * {@code "1996.07.10 AD at 15:08:56 PDT"}, if the given {@code fieldPosition}
+ * is {@link DateFormat#YEAR_FIELD}, the begin index and end index of
+ * {@code fieldPosition} will be set to 0 and 4, respectively.
+ * Notice that if the same date-time field appears more than once in a
+ * pattern, the {@code fieldPosition} will be set for the first occurrence
+ * of that date-time field. For instance, formatting a {@code Date} to the
+ * date-time string {@code "1 PM PDT (Pacific Daylight Time)"} using the
+ * pattern {@code "h a z (zzzz)"} and the alignment field
+ * {@link DateFormat#TIMEZONE_FIELD}, the begin index and end index of
+ * {@code fieldPosition} will be set to 5 and 8, respectively, for the
+ * first occurrence of the timezone pattern character {@code 'z'}.
* @return the formatted date-time string.
- * @exception NullPointerException if the given {@code date} is {@code null}.
+ * @exception NullPointerException if any of the parameters is {@code null}.
*/
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo,
@@ -1790,22 +1820,18 @@
if (i < compiledPattern.length) {
int nextTag = compiledPattern[i] >>> 8;
- if (!(nextTag == TAG_QUOTE_ASCII_CHAR ||
- nextTag == TAG_QUOTE_CHARS)) {
- obeyCount = true;
- }
+ int nextCount = compiledPattern[i] & 0xff;
+ obeyCount = shouldObeyCount(nextTag, nextCount);
if (hasFollowingMinusSign &&
(nextTag == TAG_QUOTE_ASCII_CHAR ||
nextTag == TAG_QUOTE_CHARS)) {
- int c;
- if (nextTag == TAG_QUOTE_ASCII_CHAR) {
- c = compiledPattern[i] & 0xff;
- } else {
- c = compiledPattern[i+1];
+
+ if (nextTag != TAG_QUOTE_ASCII_CHAR) {
+ nextCount = compiledPattern[i+1];
}
- if (c == minusSign) {
+ if (nextCount == minusSign) {
useFollowingMinusSignAsDelimiter = true;
}
}
@@ -1848,6 +1874,36 @@
return parsedDate;
}
+ /* If the next tag/pattern is a <Numeric_Field> then the parser
+ * should consider the count of digits while parsing the contigous digits
+ * for the current tag/pattern
+ */
+ private boolean shouldObeyCount(int tag, int count) {
+ switch (tag) {
+ case PATTERN_MONTH:
+ case PATTERN_MONTH_STANDALONE:
+ return count <= 2;
+ case PATTERN_YEAR:
+ case PATTERN_DAY_OF_MONTH:
+ case PATTERN_HOUR_OF_DAY1:
+ case PATTERN_HOUR_OF_DAY0:
+ case PATTERN_MINUTE:
+ case PATTERN_SECOND:
+ case PATTERN_MILLISECOND:
+ case PATTERN_DAY_OF_YEAR:
+ case PATTERN_DAY_OF_WEEK_IN_MONTH:
+ case PATTERN_WEEK_OF_YEAR:
+ case PATTERN_WEEK_OF_MONTH:
+ case PATTERN_HOUR1:
+ case PATTERN_HOUR0:
+ case PATTERN_WEEK_YEAR:
+ case PATTERN_ISO_DAY_OF_WEEK:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/**
* Private code-size reduction function used by subParse.
* @param text the time text being parsed.
@@ -1952,6 +2008,17 @@
// Checking long and short zones [1 & 2],
// and long and short daylight [3 & 4].
String zoneName = zoneNames[i];
+ // Android-removed: App compat. Don't retrieve more data when data missing in DFS.
+ // It may have risk of app compat issue, and no significant benefit on Android because
+ // the DFS and TimeZoneNameUtility will both come from ICU / CLDR on Android.
+ /*
+ if (zoneName.isEmpty()) {
+ // fill in by retrieving single name
+ zoneName = TimeZoneNameUtility.retrieveDisplayName(
+ zoneNames[0], i >= 3, i % 2, locale);
+ zoneNames[i] = zoneName;
+ }
+ */
if (text.regionMatches(true, start,
zoneName, 0, zoneName.length())) {
return i;
@@ -2254,6 +2321,7 @@
if (patternCharIndex == PATTERN_HOUR_OF_DAY1 ||
patternCharIndex == PATTERN_HOUR1 ||
(patternCharIndex == PATTERN_MONTH && count <= 2) ||
+ (patternCharIndex == PATTERN_MONTH_STANDALONE && count <= 2) ||
patternCharIndex == PATTERN_YEAR ||
patternCharIndex == PATTERN_WEEK_YEAR) {
// It would be good to unify this with the obeyCount logic below,
@@ -2374,6 +2442,21 @@
return index;
}
}
+ break parsing;
+
+ case PATTERN_MONTH_STANDALONE: // 'L'
+ if (count <= 2) {
+ // Don't want to parse the month if it is a string
+ // while pattern uses numeric style: L or LL
+ //[we computed 'value' above.]
+ calb.set(Calendar.MONTH, value - 1);
+ return pos.index;
+ }
+ Map<String, Integer> maps = getDisplayNamesMap(field, locale);
+ if ((index = matchString(text, start, field, maps, calb)) > 0) {
+ return index;
+ }
+ break parsing;
*/
{
final int idx = parseMonth(text, count, value, start, field, pos,
@@ -2936,7 +3019,7 @@
/**
* After reading an object from the input stream, the format
* pattern in the object is verified.
- * <p>
+ *
* @exception InvalidObjectException if the pattern is invalid
*/
private void readObject(ObjectInputStream stream)
diff --git a/ojluni/src/main/java/java/time/format/DateTimeFormatterBuilder.java b/ojluni/src/main/java/java/time/format/DateTimeFormatterBuilder.java
index fff8d9a..fde036a 100644
--- a/ojluni/src/main/java/java/time/format/DateTimeFormatterBuilder.java
+++ b/ojluni/src/main/java/java/time/format/DateTimeFormatterBuilder.java
@@ -218,8 +218,11 @@
// .getLocaleResources(locale);
// String pattern = lr.getJavaTimeDateTimePattern(
// convertStyle(timeStyle), convertStyle(dateStyle), chrono.getCalendarType());
- ExtendedCalendar extendedCalendar = ICU.getExtendedCalendar(locale,
- chrono.getCalendarType());
+
+ // "iso8601" calendar type doesn't work well for ICU due to http://b/206566562.
+ // Workaround the issue by using Gregorian calendar.
+ String calType = chrono instanceof IsoChronology ? "gregorian" : chrono.getCalendarType();
+ ExtendedCalendar extendedCalendar = ICU.getExtendedCalendar(locale, calType);
String pattern = extendedCalendar.getDateTimePattern(convertStyle(dateStyle),
convertStyle(timeStyle));
// Transform the pattern coming from ICU because DateTimeFormatter does not handle some date
diff --git a/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java b/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
index f1a0bed..fb464e9 100644
--- a/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
+++ b/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
@@ -141,7 +141,10 @@
es = ((CopyOnWriteArrayList<?>)c).getArray();
else {
es = c.toArray();
- if (c.getClass() != java.util.ArrayList.class)
+ // Android-changed: Defend against c.toArray (incorrectly) not returning Object[]
+ // (see b/204397945)
+ // if (c.getClass() != java.util.ArrayList.class)
+ if (es.getClass() != Object[].class)
es = Arrays.copyOf(es, es.length, Object[].class);
}
setArray(es);
diff --git a/ojluni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/ojluni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
index ee1c1b1..c068dee 100644
--- a/ojluni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
@@ -263,7 +263,10 @@
}
Object[] es = c.toArray();
int n = es.length;
- if (c.getClass() != java.util.ArrayList.class)
+ // Android-changed: Defend against c.toArray (incorrectly) not returning Object[]
+ // (see b/204397945)
+ // if (c.getClass() != java.util.ArrayList.class)
+ if (es.getClass() != Object[].class)
es = Arrays.copyOf(es, n, Object[].class);
if (screen && (n == 1 || this.comparator != null)) {
for (Object e : es)
diff --git a/ojluni/src/test/java/nio/channels/Selector/SelectWithConsumer.java b/ojluni/src/test/java/nio/channels/Selector/SelectWithConsumer.java
index 0c96996..0d68b38 100644
--- a/ojluni/src/test/java/nio/channels/Selector/SelectWithConsumer.java
+++ b/ojluni/src/test/java/nio/channels/Selector/SelectWithConsumer.java
@@ -592,6 +592,7 @@
/**
* Test an action invoking select()
*/
+ @Test(enabled = false)
public void testReentrantSelect1() throws Exception {
Pipe p = Pipe.open();
try (Selector sel = Selector.open()) {
@@ -621,6 +622,7 @@
/**
* Test an action invoking selectNow()
*/
+ @Test(enabled = false)
public void testReentrantSelect2() throws Exception {
Pipe p = Pipe.open();
try (Selector sel = Selector.open()) {
@@ -650,6 +652,7 @@
/**
* Test an action invoking select(Consumer)
*/
+ @Test(enabled = false)
public void testReentrantSelect3() throws Exception {
Pipe p = Pipe.open();
try (Selector sel = Selector.open()) {
diff --git a/ojluni/src/test/java/security/cert/CertPathEncodingTest.java b/ojluni/src/test/java/security/cert/CertPathEncodingTest.java
new file mode 100644
index 0000000..0b74525
--- /dev/null
+++ b/ojluni/src/test/java/security/cert/CertPathEncodingTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.security.cert;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.List;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @bug 8074931
+ * @summary CertPathEncodingTest tests the ability of the CertPath and
+ * CertificateFactory to encode and decode CertPaths.
+ */
+public final class CertPathEncodingTest {
+ /*
+ Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 935438132 (0x37c1a734)
+ Signature Algorithm: dsaWithSHA1
+ Issuer: C=us, O=sun, OU=east, OU=bcn, CN=yassir
+ Validity
+ Not Before: Aug 23 19:55:32 1999 GMT
+ Not After : Aug 22 19:55:32 2000 GMT
+ Subject: C=us, O=sun, OU=east, OU=bcn
+ Subject Public Key Info:
+ Public Key Algorithm: dsaEncryption
+ pub:
+ 63:47:4f:f6:29:e5:98:a2:21:fd:da:97:9e:3f:ca:
+ b0:17:49:8d:8a:a7:06:0d:a6:78:97:39:59:33:72:
+ a2:a5:74:d5:3a:ef:e6:7c:07:d7:8e:8e:d1:66:73:
+ 99:14:04:96:f5:31:d6:72:ee:d2:53:f8:90:b5:f3:
+ c3:f1:64:ba:1a:9e:c0:0a:da:92:48:c5:d3:84:7e:
+ 48:09:66:d9:51:ba:74:56:5a:77:8a:8c:9a:9c:f6:
+ 84:12:61:12:51:dc:c6:4f:84:94:ec:cb:78:51:83:
+ 8c:20:8a:53:7b:d2:b6:36:df:50:35:95:1f:cb:50:
+ 55:8b:3f:fb:e2:77:cb
+ P:
+ 00:fd:7f:53:81:1d:75:12:29:52:df:4a:9c:2e:ec:
+ e4:e7:f6:11:b7:52:3c:ef:44:00:c3:1e:3f:80:b6:
+ 51:26:69:45:5d:40:22:51:fb:59:3d:8d:58:fa:bf:
+ c5:f5:ba:30:f6:cb:9b:55:6c:d7:81:3b:80:1d:34:
+ 6f:f2:66:60:b7:6b:99:50:a5:a4:9f:9f:e8:04:7b:
+ 10:22:c2:4f:bb:a9:d7:fe:b7:c6:1b:f8:3b:57:e7:
+ c6:a8:a6:15:0f:04:fb:83:f6:d3:c5:1e:c3:02:35:
+ 54:13:5a:16:91:32:f6:75:f3:ae:2b:61:d7:2a:ef:
+ f2:22:03:19:9d:d1:48:01:c7
+ Q:
+ 00:97:60:50:8f:15:23:0b:cc:b2:92:b9:82:a2:eb:
+ 84:0b:f0:58:1c:f5
+ G:
+ 00:f7:e1:a0:85:d6:9b:3d:de:cb:bc:ab:5c:36:b8:
+ 57:b9:79:94:af:bb:fa:3a:ea:82:f9:57:4c:0b:3d:
+ 07:82:67:51:59:57:8e:ba:d4:59:4f:e6:71:07:10:
+ 81:80:b4:49:16:71:23:e8:4c:28:16:13:b7:cf:09:
+ 32:8c:c8:a6:e1:3c:16:7a:8b:54:7c:8d:28:e0:a3:
+ ae:1e:2b:b3:a6:75:91:6e:a3:7f:0b:fa:21:35:62:
+ f1:fb:62:7a:01:24:3b:cc:a4:f1:be:a8:51:90:89:
+ a8:83:df:e1:5a:e5:9f:06:92:8b:66:5e:80:7b:55:
+ 25:64:01:4c:3b:fe:cf:49:2a
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Key Encipherment, Certificate Sign
+ Signature Algorithm: dsaWithSHA1
+ r:
+ 52:80:52:2b:2c:3d:02:66:58:b4:dc:ef:52:26:70:
+ 1b:53:ca:b3:7d
+ s:
+ 62:03:b2:ab:3e:18:2a:66:09:b6:ce:d4:05:a5:8e:
+ a5:7a:0d:55:67
+ */
+ private static final String cert1 =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICzTCCAougAwIBAgIEN8GnNDALBgcqhkjOOAQDBQAwSTELMAkGA1UEBhMCdXMx\n" +
+ "DDAKBgNVBAoTA3N1bjENMAsGA1UECxMEZWFzdDEMMAoGA1UECxMDYmNuMQ8wDQYD\n" +
+ "VQQDEwZ5YXNzaXIwHhcNOTkwODIzMTk1NTMyWhcNMDAwODIyMTk1NTMyWjA4MQsw\n" +
+ "CQYDVQQGEwJ1czEMMAoGA1UEChMDc3VuMQ0wCwYDVQQLEwRlYXN0MQwwCgYDVQQL\n" +
+ "EwNiY24wggG1MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qcLuzk5/YR\n" +
+ "t1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZ\n" +
+ "UKWkn5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOu\n" +
+ "K2HXKu/yIgMZndFIAccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps9\n" +
+ "3su8q1w2uFe5eZSvu/o66oL5V0wLPQeCZ1FZV4661FlP5nEHEIGAtEkWcSPoTCgW\n" +
+ "E7fPCTKMyKbhPBZ6i1R8jSjgo64eK7OmdZFuo38L+iE1YvH7YnoBJDvMpPG+qFGQ\n" +
+ "iaiD3+Fa5Z8GkotmXoB7VSVkAUw7/s9JKgOBggACf2NHT/Yp5ZiiIf3al54/yrAX\n" +
+ "SY2KpwYNpniXOVkzcqKldNU67+Z8B9eOjtFmc5kUBJb1MdZy7tJT+JC188PxZLoa\n" +
+ "nsAK2pJIxdOEfkgJZtlRunRWWneKjJqc9oQSYRJR3MZPhJTsy3hRg4wgilN70rY2\n" +
+ "31A1lR/LUFWLP/vid8ujEzARMA8GA1UdDwEB/wQFAwMHpAAwCwYHKoZIzjgEAwUA\n" +
+ "Ay8AMCwCFFKAUissPQJmWLTc71ImcBtTyrN9AhRiA7KrPhgqZgm2ztQFpY6leg1V\n" +
+ "Zw==\n" +
+ "-----END CERTIFICATE-----\n" +
+ "";
+
+ /*
+ Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 935095671 (0x37bc6d77)
+ Signature Algorithm: dsaWithSHA1
+ Issuer: C=us, O=sun, OU=east, OU=bcn, CN=yassir
+ Validity
+ Not Before: Aug 19 20:47:51 1999 GMT
+ Not After : Aug 18 20:47:51 2000 GMT
+ Subject: C=us, O=sun, OU=east, OU=bcn, CN=yassir
+ Subject Public Key Info:
+ Public Key Algorithm: dsaEncryption
+ pub:
+ 0a:cc:a4:ec:d6:88:45:c2:24:6b:0d:78:f1:82:f3:
+ 5e:3e:31:5d:fb:64:d5:06:5e:39:16:f1:0a:85:d1:
+ ff:d1:a4:74:c5:e6:b0:ba:93:1c:ee:69:51:be:3b:
+ a6:66:44:50:b4:f0:5e:0e:dd:9f:08:71:fe:a1:91:
+ 2e:d4:9e:6b:b2:c0:82:3c:91:6c:18:b0:d9:bc:a3:
+ 48:91:3f:8b:59:01:61:00:02:ab:22:31:bc:7c:6c:
+ 0d:9f:ed:be:33:e6:5c:44:9e:62:30:95:f8:6d:22:
+ d7:e5:85:4c:b0:98:6e:ad:cc:ca:3b:ad:cb:fa:f7:
+ 9f:37:13:f7:ca:e2:22:ba
+ P:
+ 00:fd:7f:53:81:1d:75:12:29:52:df:4a:9c:2e:ec:
+ e4:e7:f6:11:b7:52:3c:ef:44:00:c3:1e:3f:80:b6:
+ 51:26:69:45:5d:40:22:51:fb:59:3d:8d:58:fa:bf:
+ c5:f5:ba:30:f6:cb:9b:55:6c:d7:81:3b:80:1d:34:
+ 6f:f2:66:60:b7:6b:99:50:a5:a4:9f:9f:e8:04:7b:
+ 10:22:c2:4f:bb:a9:d7:fe:b7:c6:1b:f8:3b:57:e7:
+ c6:a8:a6:15:0f:04:fb:83:f6:d3:c5:1e:c3:02:35:
+ 54:13:5a:16:91:32:f6:75:f3:ae:2b:61:d7:2a:ef:
+ f2:22:03:19:9d:d1:48:01:c7
+ Q:
+ 00:97:60:50:8f:15:23:0b:cc:b2:92:b9:82:a2:eb:
+ 84:0b:f0:58:1c:f5
+ G:
+ 00:f7:e1:a0:85:d6:9b:3d:de:cb:bc:ab:5c:36:b8:
+ 57:b9:79:94:af:bb:fa:3a:ea:82:f9:57:4c:0b:3d:
+ 07:82:67:51:59:57:8e:ba:d4:59:4f:e6:71:07:10:
+ 81:80:b4:49:16:71:23:e8:4c:28:16:13:b7:cf:09:
+ 32:8c:c8:a6:e1:3c:16:7a:8b:54:7c:8d:28:e0:a3:
+ ae:1e:2b:b3:a6:75:91:6e:a3:7f:0b:fa:21:35:62:
+ f1:fb:62:7a:01:24:3b:cc:a4:f1:be:a8:51:90:89:
+ a8:83:df:e1:5a:e5:9f:06:92:8b:66:5e:80:7b:55:
+ 25:64:01:4c:3b:fe:cf:49:2a
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Key Encipherment, Certificate Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:5
+ Signature Algorithm: dsaWithSHA1
+ r:
+ 2f:88:46:37:94:92:b2:02:07:5b:8d:76:e5:81:23:
+ 85:7f:bc:8d:b9
+ s:
+ 00:8b:d7:41:fa:11:c7:ab:27:92:5d:0a:03:98:56:
+ 36:42:5f:f5:1f:9d
+ */
+ private static final String cert2 =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIC9TCCArKgAwIBAgIEN7xtdzALBgcqhkjOOAQDBQAwSTELMAkGA1UEBhMCdXMx\n" +
+ "DDAKBgNVBAoTA3N1bjENMAsGA1UECxMEZWFzdDEMMAoGA1UECxMDYmNuMQ8wDQYD\n" +
+ "VQQDEwZ5YXNzaXIwHhcNOTkwODE5MjA0NzUxWhcNMDAwODE4MjA0NzUxWjBJMQsw\n" +
+ "CQYDVQQGEwJ1czEMMAoGA1UEChMDc3VuMQ0wCwYDVQQLEwRlYXN0MQwwCgYDVQQL\n" +
+ "EwNiY24xDzANBgNVBAMTBnlhc3NpcjCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9\n" +
+ "f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2\n" +
+ "y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD\n" +
+ "9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLr\n" +
+ "hAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrU\n" +
+ "WU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6\n" +
+ "ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKB\n" +
+ "gArMpOzWiEXCJGsNePGC814+MV37ZNUGXjkW8QqF0f/RpHTF5rC6kxzuaVG+O6Zm\n" +
+ "RFC08F4O3Z8Icf6hkS7UnmuywII8kWwYsNm8o0iRP4tZAWEAAqsiMbx8bA2f7b4z\n" +
+ "5lxEnmIwlfhtItflhUywmG6tzMo7rcv69583E/fK4iK6oycwJTAPBgNVHQ8BAf8E\n" +
+ "BQMDB6QAMBIGA1UdEwEB/wQIMAYBAf8CAQUwCwYHKoZIzjgEAwUAAzAAMC0CFC+I\n" +
+ "RjeUkrICB1uNduWBI4V/vI25AhUAi9dB+hHHqyeSXQoDmFY2Ql/1H50=\n" +
+ "-----END CERTIFICATE-----\n" +
+ "";
+
+ private static final String pkcs7path =
+ "MIIF9QYJKoZIhvcNAQcCoIIF5jCCBeICAQExADALBgkqhkiG9w0BBwGgggXKMIICzTCCAougAwIB\n" +
+ "AgIEN8GnNDALBgcqhkjOOAQDBQAwSTELMAkGA1UEBhMCdXMxDDAKBgNVBAoTA3N1bjENMAsGA1UE\n" +
+ "CxMEZWFzdDEMMAoGA1UECxMDYmNuMQ8wDQYDVQQDEwZ5YXNzaXIwHhcNOTkwODIzMTk1NTMyWhcN\n" +
+ "MDAwODIyMTk1NTMyWjA4MQswCQYDVQQGEwJ1czEMMAoGA1UEChMDc3VuMQ0wCwYDVQQLEwRlYXN0\n" +
+ "MQwwCgYDVQQLEwNiY24wggG1MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qcLuzk5/YR\n" +
+ "t1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQ\n" +
+ "IsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFIAccCFQCX\n" +
+ "YFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5eZSvu/o66oL5V0wLPQeCZ1FZ\n" +
+ "V4661FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbhPBZ6i1R8jSjgo64eK7OmdZFuo38L+iE1YvH7\n" +
+ "YnoBJDvMpPG+qFGQiaiD3+Fa5Z8GkotmXoB7VSVkAUw7/s9JKgOBggACf2NHT/Yp5ZiiIf3al54/\n" +
+ "yrAXSY2KpwYNpniXOVkzcqKldNU67+Z8B9eOjtFmc5kUBJb1MdZy7tJT+JC188PxZLoansAK2pJI\n" +
+ "xdOEfkgJZtlRunRWWneKjJqc9oQSYRJR3MZPhJTsy3hRg4wgilN70rY231A1lR/LUFWLP/vid8uj\n" +
+ "EzARMA8GA1UdDwEB/wQFAwMHpAAwCwYHKoZIzjgEAwUAAy8AMCwCFFKAUissPQJmWLTc71ImcBtT\n" +
+ "yrN9AhRiA7KrPhgqZgm2ztQFpY6leg1VZzCCAvUwggKyoAMCAQICBDe8bXcwCwYHKoZIzjgEAwUA\n" +
+ "MEkxCzAJBgNVBAYTAnVzMQwwCgYDVQQKEwNzdW4xDTALBgNVBAsTBGVhc3QxDDAKBgNVBAsTA2Jj\n" +
+ "bjEPMA0GA1UEAxMGeWFzc2lyMB4XDTk5MDgxOTIwNDc1MVoXDTAwMDgxODIwNDc1MVowSTELMAkG\n" +
+ "A1UEBhMCdXMxDDAKBgNVBAoTA3N1bjENMAsGA1UECxMEZWFzdDEMMAoGA1UECxMDYmNuMQ8wDQYD\n" +
+ "VQQDEwZ5YXNzaXIwggG3MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qcLuzk5/YRt1I8\n" +
+ "70QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQIsJP\n" +
+ "u6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFIAccCFQCXYFCP\n" +
+ "FSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5eZSvu/o66oL5V0wLPQeCZ1FZV466\n" +
+ "1FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbhPBZ6i1R8jSjgo64eK7OmdZFuo38L+iE1YvH7YnoB\n" +
+ "JDvMpPG+qFGQiaiD3+Fa5Z8GkotmXoB7VSVkAUw7/s9JKgOBhAACgYAKzKTs1ohFwiRrDXjxgvNe\n" +
+ "PjFd+2TVBl45FvEKhdH/0aR0xeawupMc7mlRvjumZkRQtPBeDt2fCHH+oZEu1J5rssCCPJFsGLDZ\n" +
+ "vKNIkT+LWQFhAAKrIjG8fGwNn+2+M+ZcRJ5iMJX4bSLX5YVMsJhurczKO63L+vefNxP3yuIiuqMn\n" +
+ "MCUwDwYDVR0PAQH/BAUDAwekADASBgNVHRMBAf8ECDAGAQH/AgEFMAsGByqGSM44BAMFAAMwADAt\n" +
+ "AhQviEY3lJKyAgdbjXblgSOFf7yNuQIVAIvXQfoRx6snkl0KA5hWNkJf9R+dMQA=\n" +
+ "";
+
+ // Runs test of CertPath encoding and decoding.
+ @Test
+ public static void testCertPathEncoding(String[] args) throws Exception {
+ // Make the CertPath whose encoded form has already been stored
+ CertificateFactory certFac = CertificateFactory.getInstance("X509");
+
+ final List<Certificate> certs = new ArrayList<>();
+ certs.add(certFac.generateCertificate(new ByteArrayInputStream(cert1.getBytes())));
+ certs.add(certFac.generateCertificate(new ByteArrayInputStream(cert2.getBytes())));
+
+ CertPath cp = certFac.generateCertPath(certs);
+
+ // Get the encoded form of the CertPath we made
+ byte[] encoded = cp.getEncoded("PKCS7");
+ // check if it matches the encoded value
+ assertEquals(Base64.getMimeDecoder().decode(pkcs7path.getBytes()), encoded,
+ "PKCS#7 encoding doesn't match stored value");
+
+ // Generate a CertPath from the encoded value and check if it equals
+ // the CertPath generated from the certificates
+ CertPath decodedCP = certFac.generateCertPath(new ByteArrayInputStream(encoded), "PKCS7");
+ assertEquals(decodedCP, cp, "CertPath decoded from PKCS#7 isn't equal to original");
+ }
+}
diff --git a/ojluni/src/test/java/security/cert/GetInstance.java b/ojluni/src/test/java/security/cert/GetInstance.java
new file mode 100644
index 0000000..c4b0284
--- /dev/null
+++ b/ojluni/src/test/java/security/cert/GetInstance.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 4416946
+ * @summary Make sure {CertStore,CertPathBuilder,CertPathValidator,
+ * CertificateFactory}.getInstance throws InvalidAlgorithmParameterException
+ * if invalid params are specified and NoSuchAlgorithmException (or
+ * CertificateException for CertificateFactory) if bogus type is specified
+ */
+package test.java.security.cert;
+
+import static org.testng.Assert.assertThrows;
+
+import java.security.*;
+import java.security.cert.*;
+import java.util.*;
+import org.testng.annotations.Test;
+
+public class GetInstance {
+
+ @Test
+ public static void testGetInstance(String[] args) throws Exception {
+
+ CollectionCertStoreParameters ccsp
+ = new CollectionCertStoreParameters(new ArrayList<>());
+ assertThrows(InvalidAlgorithmParameterException.class,
+ () -> CertStore.getInstance("LDAP", ccsp));
+
+ assertThrows(NoSuchAlgorithmException.class,
+ () -> CertStore.getInstance("BOGUS", null));
+
+ assertThrows(NoSuchAlgorithmException.class,
+ () -> CertPathBuilder.getInstance("BOGUS"));
+
+ assertThrows(NoSuchAlgorithmException.class,
+ () -> CertPathValidator.getInstance("BOGUS"));
+
+ assertThrows(CertificateException.class,
+ () -> CertificateFactory.getInstance("BOGUS"));
+ }
+}
diff --git a/ojluni/src/test/java/security/cert/X509CertSelectorTest.java b/ojluni/src/test/java/security/cert/X509CertSelectorTest.java
new file mode 100644
index 0000000..f3c0088
--- /dev/null
+++ b/ojluni/src/test/java/security/cert/X509CertSelectorTest.java
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.security.cert;
+
+import static org.testng.Assert.assertEquals;
+import static sun.security.x509.GeneralNameInterface.NAME_DIRECTORY;
+import static sun.security.x509.NameConstraintsExtension.EXCLUDED_SUBTREES;
+import static sun.security.x509.NameConstraintsExtension.PERMITTED_SUBTREES;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.testng.annotations.Test;
+
+import sun.security.util.DerInputStream;
+import sun.security.util.DerOutputStream;
+import sun.security.util.DerValue;
+import sun.security.util.ObjectIdentifier;
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+import sun.security.x509.CertificatePoliciesExtension;
+import sun.security.x509.DNSName;
+import sun.security.x509.GeneralName;
+import sun.security.x509.GeneralNameInterface;
+import sun.security.x509.GeneralNames;
+import sun.security.x509.GeneralSubtree;
+import sun.security.x509.GeneralSubtrees;
+import sun.security.x509.KeyIdentifier;
+import sun.security.x509.NameConstraintsExtension;
+import sun.security.x509.PolicyInformation;
+import sun.security.x509.PrivateKeyUsageExtension;
+import sun.security.x509.SubjectAlternativeNameExtension;
+import sun.security.x509.X500Name;
+
+/*
+ * @test
+ * @bug 8074931
+ * @summary This class tests the X509CertSelector. The tests check particular criteria
+ * by setting them to a value that should match our test certificate and
+ * ensuring that they do match, then setting them to a value that should not
+ * match our test certificate and ensuring that they do not match.
+ * @modules java.base/sun.security.x509
+ * java.base/sun.security.util
+ */
+public class X509CertSelectorTest {
+ /*
+ Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 954172088 (0x38df82b8)
+ Signature Algorithm: dsaWithSHA1
+ Issuer: C=us, O=sun, OU=testing
+ Validity
+ Not Before: Mar 27 15:48:08 2000 GMT
+ Not After : Jun 25 14:48:08 2000 GMT
+ Subject: C=us, O=sun, OU=testing, CN=mullan
+ Subject Public Key Info:
+ Public Key Algorithm: dsaEncryption
+ pub: 0
+ P: 0
+ Q: 0
+ G: 0
+ X509v3 extensions:
+ X509v3 Name Constraints: critical
+ 0D.B0@.>1.0...U....us1.0
+ ..U.
+ ..sun1.0...U....testing1.0
+ ..U....mullan
+ X509v3 Subject Key Identifier:
+ 56:E8:88:AE:9D:B5:3F:2B:CB:A0:4C:4B:E2:87:53:07:33:77:1B:DF
+ X509v3 Authority Key Identifier:
+ keyid:8E:DD:AF:6F:EE:02:12:F4:61:E9:2F:E3:64:1A:6F:71:32:25:20:C0
+
+ X509v3 Subject Alternative Name:
+ email:mullan@east.sun.com
+ X509v3 Private Key Usage Period:
+ Not Before: Jan 1 05:00:00 2000 GMT, Not After: Jan 1 05:00:00 2001 GMT
+ X509v3 Key Usage: critical
+ Digital Signature
+ X509v3 Certificate Policies:
+ 0$0\..*...0.0...+.......0..
+ Testing...
+ Signature Algorithm: dsaWithSHA1
+ r:
+ 44:c7:35:40:5d:6c:28:75:7f:73:b2:f8:0d:72:6c:
+ 09:65:b8:81:14
+ s:
+ 76:79:f5:c7:37:3b:0d:9b:db:70:2f:20:80:36:e3:
+ 80:e8:a6:c6:71
+ */
+ private static final String testCert =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICLjCCAeygAwIBAgIEON+CuDALBgcqhkjOOAQDBQAwLTELMAkGA1UEBhMCdXMx\n" +
+ "DDAKBgNVBAoTA3N1bjEQMA4GA1UECxMHdGVzdGluZzAeFw0wMDAzMjcxNTQ4MDha\n" +
+ "Fw0wMDA2MjUxNDQ4MDhaMD4xCzAJBgNVBAYTAnVzMQwwCgYDVQQKEwNzdW4xEDAO\n" +
+ "BgNVBAsTB3Rlc3RpbmcxDzANBgNVBAMTBm11bGxhbjAcMBQGByqGSM44BAEwCQIB\n" +
+ "AAIBAAIBAAMEAAIBAKOCASMwggEfMFAGA1UdHgEB/wRGMESgQjBApD4xCzAJBgNV\n" +
+ "BAYTAnVzMQwwCgYDVQQKEwNzdW4xEDAOBgNVBAsTB3Rlc3RpbmcxDzANBgNVBAMT\n" +
+ "Bm11bGxhbjAdBgNVHQ4EFgQUVuiIrp21PyvLoExL4odTBzN3G98wHwYDVR0jBBgw\n" +
+ "FoAUjt2vb+4CEvRh6S/jZBpvcTIlIMAwHgYDVR0RBBcwFYETbXVsbGFuQGVhc3Qu\n" +
+ "c3VuLmNvbTArBgNVHRAEJDAigA8yMDAwMDEwMTA1MDAwMFqBDzIwMDEwMTAxMDUw\n" +
+ "MDAwWjAPBgNVHQ8BAf8EBQMDB4AAMC0GA1UdIAQmMCQwIgYEKoSAADAaMBgGCCsG\n" +
+ "AQUFBwICMAwSClRlc3RpbmcuLi4wCwYHKoZIzjgEAwUAAy8AMCwCFETHNUBdbCh1\n" +
+ "f3Oy+A1ybAlluIEUAhR2efXHNzsNm9twLyCANuOA6KbGcQ==\n" +
+ "-----END CERTIFICATE-----\n" +
+ "";
+
+ private static final String testKey =
+ "MIIBtjCCASsGByqGSM44BAEwggEeAoGBAIVWPEkcxbxhQRCqVzg55tNqbP5j0K4kdu4bkmXvfqC5\n" +
+ "+qA75DvnfzsOJseb+9AuKXWk/DvCzFDmrY1YaU3scZC3OQEO9lEO3F4VDKOaudY6OT1SI22pAIwz\n" +
+ "j5pvq+i7zOp4xUqkQUeh/4iQSfxOT5UrFGjkcbnbpVkCXD/GxAz7AhUAjtnm3dVIddUUHl6wxpZ7\n" +
+ "GcA6gSsCgYAf/PXzQtemgIDjpFrNNSgTEKkLposBXKatAM+gUKlMUjf8SQvquqPxDtRrscGjXkoL\n" +
+ "oTkaR7/akULYFpBvUcFkeIFiCnJg8M9XhCWdLvn9MPt+jR2oxookvCb9xLtD6WvIM/wd/nZ1iK4u\n" +
+ "iY1+q85xvns/Awbtwl7oZDAwE2TUKAOBhAACgYBDc9UZ+3xsZubUZvRG5cpyJceYpJp2exOPVJXn\n" +
+ "jR4CcR+cT9bAJpFSxqE/8KtNHXxHdu4f3DU67IMOVDpugzihyzXJvNm3w2H9x+6xczHG2wjvAJeh\n" +
+ "X62EWbUatxPXFAoVKZWuUbaYaZzdWBDtNRrCuKKsLo0GFy8g2BZISuD3jw==\n" +
+ "";
+
+ // Certificate to run tests on
+ private final X509Certificate cert;
+
+ // BEGIN Android-removed: Not needed with testng
+ /*
+ public static void main(String[] args) throws Exception {
+ X509CertSelectorTest test = new X509CertSelectorTest();
+ test.doTest();
+ }
+ */
+ // END Android-removed: Not needed with testng
+
+ public X509CertSelectorTest() throws CertificateException, IOException {
+ cert = (X509Certificate) CertificateFactory.getInstance("X.509")
+ .generateCertificate(new ByteArrayInputStream(testCert.getBytes()));
+ }
+
+ // Runs the test.
+ // BEGIN Android-removed: Not needed with testng
+ /*
+ private void doTest() throws Exception {
+ System.out.println("START OF TESTS FOR " + "X509CertSelector");
+
+ testSerialNumber();
+ testIssuer();
+ testSubjectKeyIdentifier();
+ testAuthorityKeyIdentifier();
+ testCertificateValid();
+ testPrivateKeyValid();
+ testSubjectPublicKeyAlgID();
+ testKeyUsage();
+ testSubjectAltName();
+ testPolicy();
+ testPathToName();
+ testSubject();
+ testSubjectPublicKey();
+ testNameConstraints();
+ testBasicConstraints();
+ testCertificate();
+ }
+ */
+ // END Android-removed: Not needed with testng
+
+ // Tests matching on the serial number contained in the certificate.
+ @Test
+ public void testSerialNumber() {
+ System.out.println("X.509 Certificate Match on serialNumber");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ selector.setSerialNumber(new BigInteger("999999999"));
+ checkMatch(selector, cert, false);
+
+ // good match
+ selector.setSerialNumber(cert.getSerialNumber());
+ checkMatch(selector, cert, true);
+ }
+
+ // Tests matching on the issuer name contained in the certificate.
+ @Test
+ public void testIssuer() throws IOException {
+ System.out.println("X.509 Certificate Match on issuer");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ selector.setIssuer("ou=bogus,ou=east,o=sun,c=us");
+ checkMatch(selector, cert, false);
+
+ // good match
+ selector.setIssuer((cert.getIssuerX500Principal()).getName("RFC2253"));
+ checkMatch(selector, cert, true);
+ }
+
+ /*
+ * Tests matching on the subject key identifier contained in the
+ * certificate.
+ */
+ @Test
+ public void testSubjectKeyIdentifier() throws IOException {
+ System.out.println("X.509 Certificate Match on subjectKeyIdentifier");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ byte[] b = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ selector.setSubjectKeyIdentifier(b);
+ checkMatch(selector, cert, false);
+
+ // good match
+ DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.14"));
+ byte[] encoded = in.getOctetString();
+ selector.setSubjectKeyIdentifier(encoded);
+ checkMatch(selector, cert, true);
+ }
+
+ /*
+ * Tests matching on the authority key identifier contained in the
+ * certificate.
+ */
+ @Test
+ public void testAuthorityKeyIdentifier() throws IOException {
+ System.out.println("X.509 Certificate Match on authorityKeyIdentifier");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ byte[] b = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ AuthorityKeyIdentifierExtension a = new AuthorityKeyIdentifierExtension(new KeyIdentifier(b), null, null);
+ selector.setAuthorityKeyIdentifier(a.getExtensionValue());
+ checkMatch(selector, cert, false);
+
+ // good match
+ DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.35"));
+ byte[] encoded = in.getOctetString();
+ selector.setAuthorityKeyIdentifier(encoded);
+ checkMatch(selector, cert, true);
+ }
+
+ /*
+ * Tests matching on the certificate validity component contained in the
+ * certificate.
+ */
+ @Test
+ public void testCertificateValid() {
+ System.out.println("X.509 Certificate Match on certificateValid");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ Calendar cal = Calendar.getInstance();
+ cal.set(1968, 12, 31);
+ selector.setCertificateValid(cal.getTime());
+ checkMatch(selector, cert, false);
+
+ // good match
+ selector.setCertificateValid(cert.getNotBefore());
+ checkMatch(selector, cert, true);
+ }
+
+ /*
+ * Tests matching on the private key validity component contained in the
+ * certificate.
+ */
+ @Test
+ public void testPrivateKeyValid() throws IOException, CertificateException {
+ System.out.println("X.509 Certificate Match on privateKeyValid");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ Calendar cal = Calendar.getInstance();
+ cal.set(1968, 12, 31);
+ selector.setPrivateKeyValid(cal.getTime());
+ checkMatch(selector, cert, false);
+
+ // good match
+ DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.16"));
+ byte[] encoded = in.getOctetString();
+ PrivateKeyUsageExtension ext = new PrivateKeyUsageExtension(false, encoded);
+ Date validDate = (Date) ext.get(PrivateKeyUsageExtension.NOT_BEFORE);
+ selector.setPrivateKeyValid(validDate);
+ checkMatch(selector, cert, true);
+
+ }
+
+ private ObjectIdentifier getCertPubKeyAlgOID(X509Certificate xcert) throws IOException {
+ byte[] encodedKey = xcert.getPublicKey().getEncoded();
+ DerValue val = new DerValue(encodedKey);
+ if (val.tag != DerValue.tag_Sequence) {
+ throw new RuntimeException("invalid key format");
+ }
+
+ return AlgorithmId.parse(val.data.getDerValue()).getOID();
+ }
+
+ /*
+ * Tests matching on the subject public key algorithm ID component contained
+ * in the certificate.
+ */
+ @Test
+ public void testSubjectPublicKeyAlgID() throws IOException {
+ System.out.println("X.509 Certificate Match on subjectPublicKeyAlgID");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ selector.setSubjectPublicKeyAlgID("2.5.29.14");
+ checkMatch(selector, cert, false);
+
+ // good match
+ selector.setSubjectPublicKeyAlgID(getCertPubKeyAlgOID(cert).toString());
+ checkMatch(selector, cert, true);
+
+ }
+
+ // Tests matching on the key usage extension contained in the certificate.
+ @Test
+ public void testKeyUsage() {
+ System.out.println("X.509 Certificate Match on keyUsage");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ boolean[] keyUsage = { true, false, true, false, true, false, true, false };
+ selector.setKeyUsage(keyUsage);
+ System.out.println("Selector = " + selector.toString());
+ checkMatch(selector, cert, false);
+
+ // good match
+ selector.setKeyUsage(cert.getKeyUsage());
+ System.out.println("Selector = " + selector.toString());
+ checkMatch(selector, cert, true);
+ }
+
+ /*
+ * Tests matching on the subject alternative name extension contained in the
+ * certificate.
+ */
+ @Test
+ public void testSubjectAltName() throws IOException {
+ System.out.println("X.509 Certificate Match on subjectAltName");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ GeneralNameInterface dnsName = new DNSName("foo.com");
+ DerOutputStream tmp = new DerOutputStream();
+ dnsName.encode(tmp);
+ selector.addSubjectAlternativeName(2, tmp.toByteArray());
+ checkMatch(selector, cert, false);
+
+ // good match
+ DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.17"));
+ byte[] encoded = in.getOctetString();
+ SubjectAlternativeNameExtension ext = new SubjectAlternativeNameExtension(false, encoded);
+ GeneralNames names = (GeneralNames) ext.get(SubjectAlternativeNameExtension.SUBJECT_NAME);
+ GeneralName name = (GeneralName) names.get(0);
+ selector.setSubjectAlternativeNames(null);
+ DerOutputStream tmp2 = new DerOutputStream();
+ name.getName().encode(tmp2);
+ selector.addSubjectAlternativeName(name.getType(), tmp2.toByteArray());
+ checkMatch(selector, cert, true);
+
+ // good match 2 (matches at least one)
+ selector.setMatchAllSubjectAltNames(false);
+ selector.addSubjectAlternativeName(2, "foo.com");
+ checkMatch(selector, cert, true);
+ }
+
+ /*
+ * Tests matching on the policy constraints extension contained in the
+ * certificate.
+ */
+ @Test
+ public void testPolicy() throws IOException {
+ System.out.println("X.509 Certificate Match on certificatePolicies");
+ // test encoding of CertificatePoliciesExtension because we wrote the
+ // code
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ Set<String> s = new HashSet<>();
+ s.add(new String("1.2.5.7.68"));
+ selector.setPolicy(s);
+ checkMatch(selector, cert, false);
+
+ // good match
+ DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.32"));
+ CertificatePoliciesExtension ext = new CertificatePoliciesExtension(false, in.getOctetString());
+ List<PolicyInformation> policies = ext.get(CertificatePoliciesExtension.POLICIES);
+ // match on the first policy id
+ PolicyInformation policyInfo = (PolicyInformation) policies.get(0);
+ s.clear();
+ s.add(policyInfo.getPolicyIdentifier().getIdentifier().toString());
+ selector.setPolicy(s);
+ checkMatch(selector, cert, true);
+ }
+
+ /*
+ * Tests matching on the name constraints extension contained in the
+ * certificate.
+ */
+ @Test
+ public void testPathToName() throws IOException {
+ System.out.println("X.509 Certificate Match on pathToName");
+
+ X509CertSelector selector = null;
+ DerInputStream in = new DerInputStream(cert.getExtensionValue("2.5.29.30"));
+ byte[] encoded = in.getOctetString();
+ NameConstraintsExtension ext = new NameConstraintsExtension(false, encoded);
+ GeneralSubtrees permitted = (GeneralSubtrees) ext.get(PERMITTED_SUBTREES);
+ GeneralSubtrees excluded = (GeneralSubtrees) ext.get(EXCLUDED_SUBTREES);
+
+ // bad matches on pathToName within excluded subtrees
+ if (excluded != null) {
+ Iterator<GeneralSubtree> e = excluded.iterator();
+ while (e.hasNext()) {
+ GeneralSubtree tree = e.next();
+ if (tree.getName().getType() == NAME_DIRECTORY) {
+ X500Name excludedDN1 = new X500Name(tree.getName().toString());
+ X500Name excludedDN2 = new X500Name("CN=Bogus, " + tree.getName().toString());
+ DerOutputStream derDN1 = new DerOutputStream();
+ DerOutputStream derDN2 = new DerOutputStream();
+ excludedDN1.encode(derDN1);
+ excludedDN2.encode(derDN2);
+ selector = new X509CertSelector();
+ selector.addPathToName(NAME_DIRECTORY, derDN1.toByteArray());
+ checkMatch(selector, cert, false);
+ selector.setPathToNames(null);
+ selector.addPathToName(NAME_DIRECTORY, derDN2.toByteArray());
+ checkMatch(selector, cert, false);
+ }
+ }
+ }
+
+ // good matches on pathToName within permitted subtrees
+ if (permitted != null) {
+ Iterator<GeneralSubtree> e = permitted.iterator();
+ while (e.hasNext()) {
+ GeneralSubtree tree = e.next();
+ if (tree.getName().getType() == NAME_DIRECTORY) {
+ X500Name permittedDN1 = new X500Name(tree.getName().toString());
+ X500Name permittedDN2 = new X500Name("CN=good, " + tree.getName().toString());
+ DerOutputStream derDN1 = new DerOutputStream();
+ DerOutputStream derDN2 = new DerOutputStream();
+ permittedDN1.encode(derDN1);
+ permittedDN2.encode(derDN2);
+ selector = new X509CertSelector();
+ selector.addPathToName(NAME_DIRECTORY, derDN1.toByteArray());
+ checkMatch(selector, cert, true);
+ selector.setPathToNames(null);
+ selector.addPathToName(NAME_DIRECTORY, derDN2.toByteArray());
+ checkMatch(selector, cert, true);
+ }
+ }
+ }
+ }
+
+ // Tests matching on the subject name contained in the certificate.
+ @Test
+ public void testSubject() throws IOException {
+ System.out.println("X.509 Certificate Match on subject");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ selector.setSubject("ou=bogus,ou=east,o=sun,c=us");
+ checkMatch(selector, cert, false);
+
+ // good match
+ selector.setSubject(cert.getSubjectX500Principal().getName("RFC2253"));
+ checkMatch(selector, cert, true);
+ }
+
+ // Tests matching on the subject public key contained in the certificate.
+ @Test
+ public void testSubjectPublicKey() throws IOException, GeneralSecurityException {
+ System.out.println("X.509 Certificate Match on subject public key");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
+ Base64.getMimeDecoder().decode(testKey.getBytes()));
+ KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+ PublicKey pubKey = keyFactory.generatePublic(keySpec);
+ selector.setSubjectPublicKey(pubKey);
+ checkMatch(selector, cert, false);
+
+ // good match
+ selector.setSubjectPublicKey(cert.getPublicKey());
+ checkMatch(selector, cert, true);
+ }
+
+ // Tests matching on the name constraints contained in the certificate.
+ // TODO(b/203769066): Depends on updated X509Certificate implementation
+ private void testNameConstraints() throws IOException {
+ System.out.println("X.509 Certificate Match on name constraints");
+ // bad match
+ GeneralSubtrees subjectTree = new GeneralSubtrees();
+ subjectTree.add(getGeneralSubtree((X500Name) cert.getSubjectDN()));
+ NameConstraintsExtension ext = new NameConstraintsExtension((GeneralSubtrees) null, subjectTree);
+ X509CertSelector selector = new X509CertSelector();
+ selector.setNameConstraints(ext.getExtensionValue());
+ checkMatch(selector, cert, false);
+
+ // good match
+ ext = new NameConstraintsExtension(subjectTree, null);
+ selector.setNameConstraints(ext.getExtensionValue());
+ checkMatch(selector, cert, true);
+ }
+
+ // Tests matching on basic constraints.
+ @Test
+ public void testBasicConstraints() {
+ System.out.println("X.509 Certificate Match on basic constraints");
+ // bad match
+ X509CertSelector selector = new X509CertSelector();
+ int mpl = cert.getBasicConstraints();
+ selector.setBasicConstraints(0);
+ checkMatch(selector, cert, false);
+
+ // good match
+ selector.setBasicConstraints(mpl);
+ checkMatch(selector, cert, true);
+ }
+
+ // Tests certificateEquals criterion
+ @Test
+ public void testCertificate() {
+ System.out.println("X.509 Certificate Match on certificateEquals criterion");
+
+ X509CertSelector selector = new X509CertSelector();
+ // good match
+ selector.setCertificate(cert);
+ checkMatch(selector, cert, true);
+ }
+
+ private void checkMatch(X509CertSelector selector, X509Certificate cert, boolean match) {
+ boolean result = selector.match(cert);
+ assertEquals(result, match,
+ selector + " match " + cert + " is " + result + ", but expect " + match);
+ }
+
+ private static GeneralSubtree getGeneralSubtree(GeneralNameInterface gni) {
+ // Create a new GeneralSubtree with the specified name, 0 base, and
+ // unlimited length
+ GeneralName gn = new GeneralName(gni);
+ GeneralSubtree subTree = new GeneralSubtree(gn, 0, -1);
+ return subTree;
+ }
+}