diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 438cd9e1cbc..5a3eaf48d64 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, 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 @@ -48,6 +48,7 @@ import java.io.Serializable; import java.text.MessageFormat; import java.util.concurrent.ConcurrentHashMap; import java.util.spi.LocaleNameProvider; +import java.util.stream.Stream; import jdk.internal.vm.annotation.Stable; @@ -1210,6 +1211,24 @@ public final class Locale implements Cloneable, Serializable { return LocaleServiceProviderPool.getAllAvailableLocales(); } + /** + * Returns a stream of all installed locales. + * The returned stream represents the union of locales supported + * by the Java runtime environment and by installed + * {@link java.util.spi.LocaleServiceProvider LocaleServiceProvider} + * implementations. At a minimum, the returned stream must contain a + * {@code Locale} instance equal to {@link Locale#ROOT Locale.ROOT} and + * a {@code Locale} instance equal to {@link Locale#US Locale.US}. + * + * @implNote Unlike {@code getAvailableLocales()}, this method does + * not create a defensive copy of the Locale array. + * @return A stream of installed locales. + * @since 21 + */ + public static Stream availableLocales() { + return LocaleServiceProviderPool.streamAllAvailableLocales(); + } + /** * Returns a list of all 2-letter country codes defined in ISO 3166. * Can be used to obtain Locales. diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java index 3748f4ef45f..5a71d5cbbb5 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, 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 @@ -38,6 +38,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.spi.LocaleServiceProvider; +import java.util.stream.Stream; /** * An instance of this class holds a set of the third party implementations of a particular @@ -138,7 +139,6 @@ public final class LocaleServiceProviderPool { LocaleServiceProviderPool.getPool(c); all.addAll(pool.getAvailableLocaleSet()); } - allAvailableLocales = all.toArray(new Locale[0]); } @@ -147,6 +147,17 @@ public final class LocaleServiceProviderPool { } } + /** + * Returns a stream of the available locales for all the provider classes. + * This stream is constructed from all the locales + * that are provided by each provider, including the JRE. + * + * @return a stream of the available locales for all provider classes + */ + public static Stream streamAllAvailableLocales() { + return Arrays.stream(AllAvailableLocales.allAvailableLocales); + } + /** * Returns an array of available locales for all the provider classes. * This array is a merged array of all the locales that are provided by each diff --git a/test/jdk/java/util/Locale/StreamAvailableLocales.java b/test/jdk/java/util/Locale/StreamAvailableLocales.java new file mode 100644 index 00000000000..ea7757f03f0 --- /dev/null +++ b/test/jdk/java/util/Locale/StreamAvailableLocales.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, 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 + * @summary Test the implementation + * of Locale.availableLocales() + * @bug 8282319 + * @run junit StreamAvailableLocales + */ + +import java.util.Arrays; +import java.util.Locale; +import java.util.stream.Stream; +import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.Arguments; + +public class StreamAvailableLocales { + + /** + * Test to validate that the methods: Locale.getAvailableLocales() + * and Locale.availableLocales() contain the same underlying elements + */ + @Test + public void testStreamEqualsArray() { + Locale[] arrayLocales = Locale.getAvailableLocales(); + Stream streamedLocales = Locale.availableLocales(); + Locale[] convertedLocales = streamedLocales.toArray(Locale[]::new); + if (Arrays.equals(arrayLocales, convertedLocales)) { + System.out.println("$$$ Passed: The underlying elements" + + " of getAvailableLocales() and availableLocales() are the same!"); + } else { + throw new RuntimeException("$$$ Error: The underlying elements" + + " of getAvailableLocales() and availableLocales()" + + " are not the same."); + } + } + + /** + * Test to validate that the stream has the required + * Locale.ROOT and Locale.US. + */ + @ParameterizedTest + @MethodSource("requiredLocaleProvider") + public void testStreamRequirements(Locale requiredLocale, String localeName) { + if (Locale.availableLocales().anyMatch(loc -> (loc.equals(requiredLocale)))) { + System.out.printf("$$$ Passed: Stream has %s!%n", localeName); + } else { + throw new RuntimeException(String.format("$$$ Error:" + + " Stream is missing %s!", localeName)); + } + } + + // Data provider for testStreamRequirements + private static Stream requiredLocaleProvider() { + return Stream.of( + Arguments.of(Locale.ROOT, "Root locale"), + Arguments.of(Locale.US, "US locale") + ); + } +}