feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
import sun.util.spi.CalendarProvider;
/**
* An abstract parent class for the
* HostLocaleProviderAdapter/SPILocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public abstract class AuxLocaleProviderAdapter extends LocaleProviderAdapter {
/**
* SPI implementations map
*/
private ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProvider> providersMap =
new ConcurrentHashMap<>();
/**
* Getter method for Locale Service Providers
*/
@Override
public <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c) {
@SuppressWarnings("unchecked")
P lsp = (P) providersMap.get(c);
if (lsp == null) {
lsp = findInstalledProvider(c);
providersMap.putIfAbsent(c, lsp == null ? NULL_PROVIDER : lsp);
}
return lsp;
}
/**
* Real body to find an implementation for each SPI.
*
* @param <P>
* @param c
* @return
*/
protected abstract <P extends LocaleServiceProvider> P findInstalledProvider(final Class<P> c);
@Override
public BreakIteratorProvider getBreakIteratorProvider() {
return getLocaleServiceProvider(BreakIteratorProvider.class);
}
@Override
public CollatorProvider getCollatorProvider() {
return getLocaleServiceProvider(CollatorProvider.class);
}
@Override
public DateFormatProvider getDateFormatProvider() {
return getLocaleServiceProvider(DateFormatProvider.class);
}
@Override
public DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
return getLocaleServiceProvider(DateFormatSymbolsProvider.class);
}
@Override
public DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
return getLocaleServiceProvider(DecimalFormatSymbolsProvider.class);
}
@Override
public NumberFormatProvider getNumberFormatProvider() {
return getLocaleServiceProvider(NumberFormatProvider.class);
}
/**
* Getter methods for java.util.spi.* providers
*/
@Override
public CurrencyNameProvider getCurrencyNameProvider() {
return getLocaleServiceProvider(CurrencyNameProvider.class);
}
@Override
public LocaleNameProvider getLocaleNameProvider() {
return getLocaleServiceProvider(LocaleNameProvider.class);
}
@Override
public TimeZoneNameProvider getTimeZoneNameProvider() {
return getLocaleServiceProvider(TimeZoneNameProvider.class);
}
@Override
public CalendarDataProvider getCalendarDataProvider() {
return getLocaleServiceProvider(CalendarDataProvider.class);
}
@Override
public CalendarNameProvider getCalendarNameProvider() {
return getLocaleServiceProvider(CalendarNameProvider.class);
}
/**
* Getter methods for sun.util.spi.* providers
*/
@Override
public CalendarProvider getCalendarProvider() {
return getLocaleServiceProvider(CalendarProvider.class);
}
@Override
public LocaleResources getLocaleResources(Locale locale) {
return null;
}
private static Locale[] availableLocales = null;
@Override
public Locale[] getAvailableLocales() {
if (availableLocales == null) {
Set<Locale> avail = new HashSet<>();
for (Class<? extends LocaleServiceProvider> c :
LocaleServiceProviderPool.spiClasses) {
LocaleServiceProvider lsp = getLocaleServiceProvider(c);
if (lsp != null) {
avail.addAll(Arrays.asList(lsp.getAvailableLocales()));
}
}
availableLocales = avail.toArray(new Locale[0]);
}
// assuming caller won't mutate the array.
return availableLocales;
}
/**
* A dummy locale service provider that indicates there is no
* provider available
*/
private static NullProvider NULL_PROVIDER = new NullProvider();
private static class NullProvider extends LocaleServiceProvider {
@Override
public Locale[] getAvailableLocales() {
return new Locale[0];
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.Set;
/**
* An interface to return a set of available language tags supported by a
* LocaleServiceProvider.
*
* @author Masayoshi Okutsu
*/
public interface AvailableLanguageTags {
/**
* Returns a set of available language tags of a LocaleServiceProvider.
* Note that the returned set doesn't contain the language tag for
* {@code Locale.Root}.
*
* @return a Set of available language tags.
*/
public Set<String> getAvailableLanguageTags();
}

View File

@@ -0,0 +1,343 @@
/*
* Copyright (c) 1999, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.util.locale.provider;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.MissingResourceException;
import sun.text.CompactByteArray;
import sun.text.SupplementaryCharacterData;
/**
* This is the class that represents the list of known words used by
* DictionaryBasedBreakIterator. The conceptual data structure used
* here is a trie: there is a node hanging off the root node for every
* letter that can start a word. Each of these nodes has a node hanging
* off of it for every letter that can be the second letter of a word
* if this node is the first letter, and so on. The trie is represented
* as a two-dimensional array that can be treated as a table of state
* transitions. Indexes are used to compress this array, taking
* advantage of the fact that this array will always be very sparse.
*/
class BreakDictionary {
//=========================================================================
// data members
//=========================================================================
/**
* The version of the dictionary that was read in.
*/
private static int supportedVersion = 1;
/**
* Maps from characters to column numbers. The main use of this is to
* avoid making room in the array for empty columns.
*/
private CompactByteArray columnMap = null;
private SupplementaryCharacterData supplementaryCharColumnMap = null;
/**
* The number of actual columns in the table
*/
private int numCols;
/**
* Columns are organized into groups of 32. This says how many
* column groups. (We could calculate this, but we store the
* value to avoid having to repeatedly calculate it.)
*/
private int numColGroups;
/**
* The actual compressed state table. Each conceptual row represents
* a state, and the cells in it contain the row numbers of the states
* to transition to for each possible letter. 0 is used to indicate
* an illegal combination of letters (i.e., the error state). The
* table is compressed by eliminating all the unpopulated (i.e., zero)
* cells. Multiple conceptual rows can then be doubled up in a single
* physical row by sliding them up and possibly shifting them to one
* side or the other so the populated cells don't collide. Indexes
* are used to identify unpopulated cells and to locate populated cells.
*/
private short[] table = null;
/**
* This index maps logical row numbers to physical row numbers
*/
private short[] rowIndex = null;
/**
* A bitmap is used to tell which cells in the comceptual table are
* populated. This array contains all the unique bit combinations
* in that bitmap. If the table is more than 32 columns wide,
* successive entries in this array are used for a single row.
*/
private int[] rowIndexFlags = null;
/**
* This index maps from a logical row number into the bitmap table above.
* (This keeps us from storing duplicate bitmap combinations.) Since there
* are a lot of rows with only one populated cell, instead of wasting space
* in the bitmap table, we just store a negative number in this index for
* rows with one populated cell. The absolute value of that number is
* the column number of the populated cell.
*/
private short[] rowIndexFlagsIndex = null;
/**
* For each logical row, this index contains a constant that is added to
* the logical column number to get the physical column number
*/
private byte[] rowIndexShifts = null;
//=========================================================================
// deserialization
//=========================================================================
BreakDictionary(String dictionaryName)
throws IOException, MissingResourceException {
readDictionaryFile(dictionaryName);
}
private void readDictionaryFile(final String dictionaryName)
throws IOException, MissingResourceException {
BufferedInputStream in;
try {
in = AccessController.doPrivileged(
new PrivilegedExceptionAction<BufferedInputStream>() {
@Override
public BufferedInputStream run() throws Exception {
return new BufferedInputStream(getClass().getResourceAsStream("/sun/text/resources/" + dictionaryName));
}
}
);
}
catch (PrivilegedActionException e) {
throw new InternalError(e.toString(), e);
}
byte[] buf = new byte[8];
if (in.read(buf) != 8) {
throw new MissingResourceException("Wrong data length",
dictionaryName, "");
}
// check version
int version = RuleBasedBreakIterator.getInt(buf, 0);
if (version != supportedVersion) {
throw new MissingResourceException("Dictionary version(" + version + ") is unsupported",
dictionaryName, "");
}
// get data size
int len = RuleBasedBreakIterator.getInt(buf, 4);
buf = new byte[len];
if (in.read(buf) != len) {
throw new MissingResourceException("Wrong data length",
dictionaryName, "");
}
// close the stream
in.close();
int l;
int offset = 0;
// read in the column map for BMP characteres (this is serialized in
// its internal form: an index array followed by a data array)
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
short[] temp = new short[l];
for (int i = 0; i < l; i++, offset+=2) {
temp[i] = RuleBasedBreakIterator.getShort(buf, offset);
}
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
byte[] temp2 = new byte[l];
for (int i = 0; i < l; i++, offset++) {
temp2[i] = buf[offset];
}
columnMap = new CompactByteArray(temp, temp2);
// read in numCols and numColGroups
numCols = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
numColGroups = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
// read in the row-number index
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
rowIndex = new short[l];
for (int i = 0; i < l; i++, offset+=2) {
rowIndex[i] = RuleBasedBreakIterator.getShort(buf, offset);
}
// load in the populated-cells bitmap: index first, then bitmap list
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
rowIndexFlagsIndex = new short[l];
for (int i = 0; i < l; i++, offset+=2) {
rowIndexFlagsIndex[i] = RuleBasedBreakIterator.getShort(buf, offset);
}
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
rowIndexFlags = new int[l];
for (int i = 0; i < l; i++, offset+=4) {
rowIndexFlags[i] = RuleBasedBreakIterator.getInt(buf, offset);
}
// load in the row-shift index
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
rowIndexShifts = new byte[l];
for (int i = 0; i < l; i++, offset++) {
rowIndexShifts[i] = buf[offset];
}
// load in the actual state table
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
table = new short[l];
for (int i = 0; i < l; i++, offset+=2) {
table[i] = RuleBasedBreakIterator.getShort(buf, offset);
}
// finally, prepare the column map for supplementary characters
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
int[] temp3 = new int[l];
for (int i = 0; i < l; i++, offset+=4) {
temp3[i] = RuleBasedBreakIterator.getInt(buf, offset);
}
supplementaryCharColumnMap = new SupplementaryCharacterData(temp3);
}
//=========================================================================
// access to the words
//=========================================================================
/**
* Uses the column map to map the character to a column number, then
* passes the row and column number to getNextState()
* @param row The current state
* @param ch The character whose column we're interested in
* @return The new state to transition to
*/
public final short getNextStateFromCharacter(int row, int ch) {
int col;
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
col = columnMap.elementAt((char)ch);
} else {
col = supplementaryCharColumnMap.getValue(ch);
}
return getNextState(row, col);
}
/**
* Returns the value in the cell with the specified (logical) row and
* column numbers. In DictionaryBasedBreakIterator, the row number is
* a state number, the column number is an input, and the return value
* is the row number of the new state to transition to. (0 is the
* "error" state, and -1 is the "end of word" state in a dictionary)
* @param row The row number of the current state
* @param col The column number of the input character (0 means "not a
* dictionary character")
* @return The row number of the new state to transition to
*/
public final short getNextState(int row, int col) {
if (cellIsPopulated(row, col)) {
// we map from logical to physical row number by looking up the
// mapping in rowIndex; we map from logical column number to
// physical column number by looking up a shift value for this
// logical row and offsetting the logical column number by
// the shift amount. Then we can use internalAt() to actually
// get the value out of the table.
return internalAt(rowIndex[row], col + rowIndexShifts[row]);
}
else {
return 0;
}
}
/**
* Given (logical) row and column numbers, returns true if the
* cell in that position is populated
*/
private boolean cellIsPopulated(int row, int col) {
// look up the entry in the bitmap index for the specified row.
// If it's a negative number, it's the column number of the only
// populated cell in the row
if (rowIndexFlagsIndex[row] < 0) {
return col == -rowIndexFlagsIndex[row];
}
// if it's a positive number, it's the offset of an entry in the bitmap
// list. If the table is more than 32 columns wide, the bitmap is stored
// successive entries in the bitmap list, so we have to divide the column
// number by 32 and offset the number we got out of the index by the result.
// Once we have the appropriate piece of the bitmap, test the appropriate
// bit and return the result.
else {
int flags = rowIndexFlags[rowIndexFlagsIndex[row] + (col >> 5)];
return (flags & (1 << (col & 0x1f))) != 0;
}
}
/**
* Implementation of getNextState() when we know the specified cell is
* populated.
* @param row The PHYSICAL row number of the cell
* @param col The PHYSICAL column number of the cell
* @return The value stored in the cell
*/
private short internalAt(int row, int col) {
// the table is a one-dimensional array, so this just does the math necessary
// to treat it as a two-dimensional array (we don't just use a two-dimensional
// array because two-dimensional arrays are inefficient in Java)
return table[row * numCols + col];
}
}

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 1999, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.io.IOException;
import java.text.BreakIterator;
import java.text.spi.BreakIteratorProvider;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.BreakIteratorProvider
* BreakIteratorProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class BreakIteratorProviderImpl extends BreakIteratorProvider
implements AvailableLanguageTags {
private static final int CHARACTER_INDEX = 0;
private static final int WORD_INDEX = 1;
private static final int LINE_INDEX = 2;
private static final int SENTENCE_INDEX = 3;
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public BreakIteratorProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
/**
* Returns a new <code>BreakIterator</code> instance
* for <a href="../BreakIterator.html#word">word breaks</a>
* for the given locale.
* @param locale the desired locale
* @return A break iterator for word breaks
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.BreakIterator#getWordInstance(java.util.Locale)
*/
@Override
public BreakIterator getWordInstance(Locale locale) {
return getBreakInstance(locale,
WORD_INDEX,
"WordData",
"WordDictionary");
}
/**
* Returns a new <code>BreakIterator</code> instance
* for <a href="../BreakIterator.html#line">line breaks</a>
* for the given locale.
* @param locale the desired locale
* @return A break iterator for line breaks
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.BreakIterator#getLineInstance(java.util.Locale)
*/
@Override
public BreakIterator getLineInstance(Locale locale) {
return getBreakInstance(locale,
LINE_INDEX,
"LineData",
"LineDictionary");
}
/**
* Returns a new <code>BreakIterator</code> instance
* for <a href="../BreakIterator.html#character">character breaks</a>
* for the given locale.
* @param locale the desired locale
* @return A break iterator for character breaks
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.BreakIterator#getCharacterInstance(java.util.Locale)
*/
@Override
public BreakIterator getCharacterInstance(Locale locale) {
return getBreakInstance(locale,
CHARACTER_INDEX,
"CharacterData",
"CharacterDictionary");
}
/**
* Returns a new <code>BreakIterator</code> instance
* for <a href="../BreakIterator.html#sentence">sentence breaks</a>
* for the given locale.
* @param locale the desired locale
* @return A break iterator for sentence breaks
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.BreakIterator#getSentenceInstance(java.util.Locale)
*/
@Override
public BreakIterator getSentenceInstance(Locale locale) {
return getBreakInstance(locale,
SENTENCE_INDEX,
"SentenceData",
"SentenceDictionary");
}
private BreakIterator getBreakInstance(Locale locale,
int type,
String dataName,
String dictionaryName) {
if (locale == null) {
throw new NullPointerException();
}
LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale);
String[] classNames = (String[]) lr.getBreakIteratorInfo("BreakIteratorClasses");
String dataFile = (String) lr.getBreakIteratorInfo(dataName);
try {
switch (classNames[type]) {
case "RuleBasedBreakIterator":
return new RuleBasedBreakIterator(dataFile);
case "DictionaryBasedBreakIterator":
String dictionaryFile = (String) lr.getBreakIteratorInfo(dictionaryName);
return new DictionaryBasedBreakIterator(dataFile, dictionaryFile);
default:
throw new IllegalArgumentException("Invalid break iterator class \"" +
classNames[type] + "\"");
}
} catch (IOException | MissingResourceException | IllegalArgumentException e) {
throw new InternalError(e.toString(), e);
}
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.Locale;
import java.util.Set;
import java.util.spi.CalendarDataProvider;
/**
* Concrete implementation of the {@link java.util.spi.CalendarDataProvider
* CalendarDataProvider} class for the JRE LocaleProviderAdapter.
*
* @author Masayoshi Okutsu
* @author Naoto Sato
*/
public class CalendarDataProviderImpl extends CalendarDataProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CalendarDataProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
@Override
public int getFirstDayOfWeek(Locale locale) {
return LocaleProviderAdapter.forType(type).getLocaleResources(locale)
.getCalendarData(CalendarDataUtility.FIRST_DAY_OF_WEEK);
}
@Override
public int getMinimalDaysInFirstWeek(Locale locale) {
return LocaleProviderAdapter.forType(type).getLocaleResources(locale)
.getCalendarData(CalendarDataUtility.MINIMAL_DAYS_IN_FIRST_WEEK);
}
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import static java.util.Calendar.*;
import java.util.Locale;
import java.util.Map;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
/**
* {@code CalendarDataUtility} is a utility class for calling the
* {@link CalendarDataProvider} methods.
*
* @author Masayoshi Okutsu
* @author Naoto Sato
*/
public class CalendarDataUtility {
public final static String FIRST_DAY_OF_WEEK = "firstDayOfWeek";
public final static String MINIMAL_DAYS_IN_FIRST_WEEK = "minimalDaysInFirstWeek";
// No instantiation
private CalendarDataUtility() {
}
public static int retrieveFirstDayOfWeek(Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarDataProvider.class);
Integer value = pool.getLocalizedObject(CalendarWeekParameterGetter.INSTANCE,
locale, FIRST_DAY_OF_WEEK);
return (value != null && (value >= SUNDAY && value <= SATURDAY)) ? value : SUNDAY;
}
public static int retrieveMinimalDaysInFirstWeek(Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarDataProvider.class);
Integer value = pool.getLocalizedObject(CalendarWeekParameterGetter.INSTANCE,
locale, MINIMAL_DAYS_IN_FIRST_WEEK);
return (value != null && (value >= 1 && value <= 7)) ? value : 1;
}
public static String retrieveFieldValueName(String id, int field, int value, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
return pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
field, value, style, false);
}
public static String retrieveJavaTimeFieldValueName(String id, int field, int value, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
String name;
name = pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
field, value, style, true);
if (name == null) {
name = pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
field, value, style, false);
}
return name;
}
public static Map<String, Integer> retrieveFieldValueNames(String id, int field, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
return pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
normalizeCalendarType(id), field, style, false);
}
public static Map<String, Integer> retrieveJavaTimeFieldValueNames(String id, int field, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
Map<String, Integer> map;
map = pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
normalizeCalendarType(id), field, style, true);
if (map == null) {
map = pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
normalizeCalendarType(id), field, style, false);
}
return map;
}
static String normalizeCalendarType(String requestID) {
String type;
if (requestID.equals("gregorian") || requestID.equals("iso8601")) {
type = "gregory";
} else if (requestID.startsWith("islamic")) {
type = "islamic";
} else {
type = requestID;
}
return type;
}
/**
* Obtains a localized field value string from a CalendarDataProvider
* implementation.
*/
private static class CalendarFieldValueNameGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarNameProvider,
String> {
private static final CalendarFieldValueNameGetter INSTANCE =
new CalendarFieldValueNameGetter();
@Override
public String getObject(CalendarNameProvider calendarNameProvider,
Locale locale,
String requestID, // calendarType
Object... params) {
assert params.length == 4;
int field = (int) params[0];
int value = (int) params[1];
int style = (int) params[2];
boolean javatime = (boolean) params[3];
// If javatime is true, resources from CLDR have precedence over JRE
// native resources.
if (javatime && calendarNameProvider instanceof CalendarNameProviderImpl) {
String name;
name = ((CalendarNameProviderImpl)calendarNameProvider)
.getJavaTimeDisplayName(requestID, field, value, style, locale);
return name;
}
return calendarNameProvider.getDisplayName(requestID, field, value, style, locale);
}
}
/**
* Obtains a localized field-value pairs from a CalendarDataProvider
* implementation.
*/
private static class CalendarFieldValueNamesMapGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarNameProvider,
Map<String, Integer>> {
private static final CalendarFieldValueNamesMapGetter INSTANCE =
new CalendarFieldValueNamesMapGetter();
@Override
public Map<String, Integer> getObject(CalendarNameProvider calendarNameProvider,
Locale locale,
String requestID, // calendarType
Object... params) {
assert params.length == 3;
int field = (int) params[0];
int style = (int) params[1];
boolean javatime = (boolean) params[2];
// If javatime is true, resources from CLDR have precedence over JRE
// native resources.
if (javatime && calendarNameProvider instanceof CalendarNameProviderImpl) {
Map<String, Integer> map;
map = ((CalendarNameProviderImpl)calendarNameProvider)
.getJavaTimeDisplayNames(requestID, field, style, locale);
return map;
}
return calendarNameProvider.getDisplayNames(requestID, field, style, locale);
}
}
private static class CalendarWeekParameterGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarDataProvider,
Integer> {
private static final CalendarWeekParameterGetter INSTANCE =
new CalendarWeekParameterGetter();
@Override
public Integer getObject(CalendarDataProvider calendarDataProvider,
Locale locale,
String requestID, // resource key
Object... params) {
assert params.length == 0;
int value;
switch (requestID) {
case FIRST_DAY_OF_WEEK:
value = calendarDataProvider.getFirstDayOfWeek(locale);
break;
case MINIMAL_DAYS_IN_FIRST_WEEK:
value = calendarDataProvider.getMinimalDaysInFirstWeek(locale);
break;
default:
throw new InternalError("invalid requestID: " + requestID);
}
return (value != 0) ? value : null;
}
}
}

View File

@@ -0,0 +1,352 @@
/*
* Copyright (c) 2012, 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import static java.util.Calendar.*;
import java.util.Comparator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.spi.CalendarNameProvider;
import sun.util.calendar.CalendarSystem;
import sun.util.calendar.Era;
/**
* Concrete implementation of the {@link java.util.spi.CalendarNameProvider
* CalendarNameProvider} class for the JRE LocaleProviderAdapter.
*
* @author Masayoshi Okutsu
* @author Naoto Sato
*/
public class CalendarNameProviderImpl extends CalendarNameProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CalendarNameProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
@Override
public String getDisplayName(String calendarType, int field, int value, int style, Locale locale) {
return getDisplayNameImpl(calendarType, field, value, style, locale, false);
}
public String getJavaTimeDisplayName(String calendarType, int field, int value, int style, Locale locale) {
return getDisplayNameImpl(calendarType, field, value, style, locale, true);
}
public String getDisplayNameImpl(String calendarType, int field, int value, int style, Locale locale, boolean javatime) {
String name = null;
String key = getResourceKey(calendarType, field, style, javatime);
if (key != null) {
LocaleResources lr = LocaleProviderAdapter.forType(type).getLocaleResources(locale);
String[] strings = javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key);
if (strings != null && strings.length > 0) {
if (field == DAY_OF_WEEK || field == YEAR) {
--value;
}
if (value < 0) {
return null;
} else if (value >= strings.length) {
if (field == ERA && "japanese".equals(calendarType)) {
Era[] jeras = CalendarSystem.forName("japanese").getEras();
if (value <= jeras.length) {
// Localized era name could not be retrieved from this provider.
// This can occur either for Reiwa or SupEra.
//
// If it's CLDR provider, try COMPAT first, which is guaranteed to have
// the name for Reiwa.
if (type == LocaleProviderAdapter.Type.CLDR) {
lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale);
key = getResourceKeyFor(LocaleProviderAdapter.Type.JRE,
calendarType, field, style, javatime);
strings =
javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key);
}
if (strings == null || value >= strings.length) {
// Get the default name for SupEra
Era supEra = jeras[value - 1]; // 0-based index
if (javatime) {
return getBaseStyle(style) == NARROW_FORMAT ?
supEra.getAbbreviation() :
supEra.getName();
} else {
return (style & LONG) != 0 ?
supEra.getName() :
supEra.getAbbreviation();
}
}
} else {
return null;
}
} else {
return null;
}
}
name = strings[value];
// If name is empty in standalone, try its `format' style.
if (name.length() == 0
&& (style == SHORT_STANDALONE || style == LONG_STANDALONE
|| style == NARROW_STANDALONE)) {
name = getDisplayName(calendarType, field, value,
getBaseStyle(style),
locale);
}
}
}
return name;
}
private static int[] REST_OF_STYLES = {
SHORT_STANDALONE, LONG_FORMAT, LONG_STANDALONE,
NARROW_FORMAT, NARROW_STANDALONE
};
@Override
public Map<String, Integer> getDisplayNames(String calendarType, int field, int style, Locale locale) {
Map<String, Integer> names;
if (style == ALL_STYLES) {
names = getDisplayNamesImpl(calendarType, field, SHORT_FORMAT, locale, false);
for (int st : REST_OF_STYLES) {
names.putAll(getDisplayNamesImpl(calendarType, field, st, locale, false));
}
} else {
// specific style
names = getDisplayNamesImpl(calendarType, field, style, locale, false);
}
return names.isEmpty() ? null : names;
}
// NOTE: This method should be used ONLY BY JSR 310 classes.
public Map<String, Integer> getJavaTimeDisplayNames(String calendarType, int field, int style, Locale locale) {
Map<String, Integer> names;
names = getDisplayNamesImpl(calendarType, field, style, locale, true);
return names.isEmpty() ? null : names;
}
private Map<String, Integer> getDisplayNamesImpl(String calendarType, int field,
int style, Locale locale, boolean javatime) {
String key = getResourceKey(calendarType, field, style, javatime);
Map<String, Integer> map = new TreeMap<>(LengthBasedComparator.INSTANCE);
if (key != null) {
LocaleResources lr = LocaleProviderAdapter.forType(type).getLocaleResources(locale);
String[] strings = javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key);
if (strings != null) {
if (!hasDuplicates(strings)) {
if (field == YEAR) {
if (strings.length > 0) {
map.put(strings[0], 1);
}
} else {
int base = (field == DAY_OF_WEEK) ? 1 : 0;
for (int i = 0; i < strings.length; i++) {
String name = strings[i];
// Ignore any empty string (some standalone month names
// are not defined)
if (name.length() == 0) {
continue;
}
map.put(name, base + i);
}
}
}
}
}
return map;
}
private static int getBaseStyle(int style) {
return style & ~(SHORT_STANDALONE - SHORT_FORMAT);
}
/**
* Comparator implementation for TreeMap which iterates keys from longest
* to shortest.
*/
private static class LengthBasedComparator implements Comparator<String> {
private static final LengthBasedComparator INSTANCE = new LengthBasedComparator();
private LengthBasedComparator() {
}
@Override
public int compare(String o1, String o2) {
int n = o2.length() - o1.length();
return (n == 0) ? o1.compareTo(o2) : n;
}
}
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
if (Locale.ROOT.equals(locale)) {
return true;
}
String calendarType = null;
if (locale.hasExtensions()) {
calendarType = locale.getUnicodeLocaleType("ca");
locale = locale.stripExtensions();
}
if (calendarType != null) {
switch (calendarType) {
case "buddhist":
case "japanese":
case "gregory":
case "islamic":
case "roc":
break;
default:
// Unknown calendar type
return false;
}
}
if (langtags.contains(locale.toLanguageTag())) {
return true;
}
if (type == LocaleProviderAdapter.Type.JRE) {
String oldname = locale.toString().replace('_', '-');
return langtags.contains(oldname);
}
return false;
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
private boolean hasDuplicates(String[] strings) {
int len = strings.length;
for (int i = 0; i < len - 1; i++) {
String a = strings[i];
if (a != null) {
for (int j = i + 1; j < len; j++) {
if (a.equals(strings[j])) {
return true;
}
}
}
}
return false;
}
private String getResourceKey(String type, int field, int style, boolean javatime) {
return getResourceKeyFor(this.type, type, field, style, javatime);
}
private static String getResourceKeyFor(LocaleProviderAdapter.Type adapterType,
String type, int field, int style, boolean javatime) {
int baseStyle = getBaseStyle(style);
boolean isStandalone = (style != baseStyle);
if ("gregory".equals(type)) {
type = null;
}
boolean isNarrow = (baseStyle == NARROW_FORMAT);
StringBuilder key = new StringBuilder();
// If javatime is true, use prefix "java.time.".
if (javatime) {
key.append("java.time.");
}
switch (field) {
case ERA:
if (type != null) {
key.append(type).append('.');
}
if (isNarrow) {
key.append("narrow.");
} else {
// JRE and CLDR use different resource key conventions
// due to historical reasons. (JRE DateFormatSymbols.getEras returns
// abbreviations while other getShort*() return abbreviations.)
if (adapterType == LocaleProviderAdapter.Type.JRE) {
if (javatime) {
if (baseStyle == LONG) {
key.append("long.");
}
}
if (baseStyle == SHORT) {
key.append("short.");
}
} else { // this.type == LocaleProviderAdapter.Type.CLDR
if (baseStyle == LONG) {
key.append("long.");
}
}
}
key.append("Eras");
break;
case YEAR:
if (!isNarrow) {
key.append(type).append(".FirstYear");
}
break;
case MONTH:
if ("islamic".equals(type)) {
key.append(type).append('.');
}
if (isStandalone) {
key.append("standalone.");
}
key.append("Month").append(toStyleName(baseStyle));
break;
case DAY_OF_WEEK:
// support standalone narrow day names
if (isStandalone && isNarrow) {
key.append("standalone.");
}
key.append("Day").append(toStyleName(baseStyle));
break;
case AM_PM:
if (isNarrow) {
key.append("narrow.");
}
key.append("AmPmMarkers");
break;
}
return key.length() > 0 ? key.toString() : null;
}
private static String toStyleName(int baseStyle) {
switch (baseStyle) {
case SHORT:
return "Abbreviations";
case NARROW_FORMAT:
return "Narrows";
}
return "Names";
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.Calendar;
import java.util.Calendar.Builder;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import sun.util.spi.CalendarProvider;
/**
* Concrete implementation of the {@link sun.util.spi.CalendarProvider
* CalendarProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
*/
public class CalendarProviderImpl extends CalendarProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CalendarProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
// Support any locales.
return true;
}
/**
* Returns a new <code>Calendar</code> instance for the
* specified locale.
*
* @param zone the time zone
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a <code>Calendar</code> instance.
* @see java.util.Calendar#getInstance(java.util.Locale)
*/
@Override
public Calendar getInstance(TimeZone zone, Locale locale) {
return new Calendar.Builder()
.setLocale(locale)
.setTimeZone(zone)
.setInstant(System.currentTimeMillis())
.build();
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,277 @@
/*
* Copyright (c) 1996, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
* (C) Copyright Taligent, Inc. 1996,1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
*
* The original version of this source code and documentation is copyrighted
* and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
* materials are provided under terms of a License Agreement between Taligent
* and Sun. This technology is protected by multiple US and International
* patents. This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package sun.util.locale.provider;
/**
* CollationRules contains the default en_US collation rules as a base
* for building other collation tables.
* <p>Note that decompositions are done before these rules are used,
* so they do not have to contain accented characters, such as A-grave.
* @see RuleBasedCollator
* @see LocaleElements
* @author Helena Shih, Mark Davis
*/
final class CollationRules {
final static String DEFAULTRULES =
"" // no FRENCH accent order by default, add in French Delta
// IGNORABLES (up to first < character)
// COMPLETELY IGNORE format characters
+ "='\u200B'=\u200C=\u200D=\u200E=\u200F"
// Control Characters
+ "=\u0000 =\u0001 =\u0002 =\u0003 =\u0004" //null, .. eot
+ "=\u0005 =\u0006 =\u0007 =\u0008 ='\u0009'" //enq, ...
+ "='\u000b' =\u000e" //vt,, so
+ "=\u000f ='\u0010' =\u0011 =\u0012 =\u0013" //si, dle, dc1, dc2, dc3
+ "=\u0014 =\u0015 =\u0016 =\u0017 =\u0018" //dc4, nak, syn, etb, can
+ "=\u0019 =\u001a =\u001b =\u001c =\u001d" //em, sub, esc, fs, gs
+ "=\u001e =\u001f =\u007f" //rs, us, del
//....then the C1 Latin 1 reserved control codes
+ "=\u0080 =\u0081 =\u0082 =\u0083 =\u0084 =\u0085"
+ "=\u0086 =\u0087 =\u0088 =\u0089 =\u008a =\u008b"
+ "=\u008c =\u008d =\u008e =\u008f =\u0090 =\u0091"
+ "=\u0092 =\u0093 =\u0094 =\u0095 =\u0096 =\u0097"
+ "=\u0098 =\u0099 =\u009a =\u009b =\u009c =\u009d"
+ "=\u009e =\u009f"
// IGNORE except for secondary, tertiary difference
// Spaces
+ ";'\u0020';'\u00A0'" // spaces
+ ";'\u2000';'\u2001';'\u2002';'\u2003';'\u2004'" // spaces
+ ";'\u2005';'\u2006';'\u2007';'\u2008';'\u2009'" // spaces
+ ";'\u200A';'\u3000';'\uFEFF'" // spaces
+ ";'\r' ;'\t' ;'\n';'\f';'\u000b'" // whitespace
// Non-spacing accents
+ ";\u0301" // non-spacing acute accent
+ ";\u0300" // non-spacing grave accent
+ ";\u0306" // non-spacing breve accent
+ ";\u0302" // non-spacing circumflex accent
+ ";\u030c" // non-spacing caron/hacek accent
+ ";\u030a" // non-spacing ring above accent
+ ";\u030d" // non-spacing vertical line above
+ ";\u0308" // non-spacing diaeresis accent
+ ";\u030b" // non-spacing double acute accent
+ ";\u0303" // non-spacing tilde accent
+ ";\u0307" // non-spacing dot above/overdot accent
+ ";\u0304" // non-spacing macron accent
+ ";\u0337" // non-spacing short slash overlay (overstruck diacritic)
+ ";\u0327" // non-spacing cedilla accent
+ ";\u0328" // non-spacing ogonek accent
+ ";\u0323" // non-spacing dot-below/underdot accent
+ ";\u0332" // non-spacing underscore/underline accent
// with the rest of the general diacritical marks in binary order
+ ";\u0305" // non-spacing overscore/overline
+ ";\u0309" // non-spacing hook above
+ ";\u030e" // non-spacing double vertical line above
+ ";\u030f" // non-spacing double grave
+ ";\u0310" // non-spacing chandrabindu
+ ";\u0311" // non-spacing inverted breve
+ ";\u0312" // non-spacing turned comma above/cedilla above
+ ";\u0313" // non-spacing comma above
+ ";\u0314" // non-spacing reversed comma above
+ ";\u0315" // non-spacing comma above right
+ ";\u0316" // non-spacing grave below
+ ";\u0317" // non-spacing acute below
+ ";\u0318" // non-spacing left tack below
+ ";\u0319" // non-spacing tack below
+ ";\u031a" // non-spacing left angle above
+ ";\u031b" // non-spacing horn
+ ";\u031c" // non-spacing left half ring below
+ ";\u031d" // non-spacing up tack below
+ ";\u031e" // non-spacing down tack below
+ ";\u031f" // non-spacing plus sign below
+ ";\u0320" // non-spacing minus sign below
+ ";\u0321" // non-spacing palatalized hook below
+ ";\u0322" // non-spacing retroflex hook below
+ ";\u0324" // non-spacing double dot below
+ ";\u0325" // non-spacing ring below
+ ";\u0326" // non-spacing comma below
+ ";\u0329" // non-spacing vertical line below
+ ";\u032a" // non-spacing bridge below
+ ";\u032b" // non-spacing inverted double arch below
+ ";\u032c" // non-spacing hacek below
+ ";\u032d" // non-spacing circumflex below
+ ";\u032e" // non-spacing breve below
+ ";\u032f" // non-spacing inverted breve below
+ ";\u0330" // non-spacing tilde below
+ ";\u0331" // non-spacing macron below
+ ";\u0333" // non-spacing double underscore
+ ";\u0334" // non-spacing tilde overlay
+ ";\u0335" // non-spacing short bar overlay
+ ";\u0336" // non-spacing long bar overlay
+ ";\u0338" // non-spacing long slash overlay
+ ";\u0339" // non-spacing right half ring below
+ ";\u033a" // non-spacing inverted bridge below
+ ";\u033b" // non-spacing square below
+ ";\u033c" // non-spacing seagull below
+ ";\u033d" // non-spacing x above
+ ";\u033e" // non-spacing vertical tilde
+ ";\u033f" // non-spacing double overscore
//+ ";\u0340" // non-spacing grave tone mark == \u0300
//+ ";\u0341" // non-spacing acute tone mark == \u0301
+ ";\u0342;"
//+ "\u0343;" // == \u0313
+ "\u0344;\u0345;\u0360;\u0361" // newer
+ ";\u0483;\u0484;\u0485;\u0486" // Cyrillic accents
+ ";\u20D0;\u20D1;\u20D2" // symbol accents
+ ";\u20D3;\u20D4;\u20D5" // symbol accents
+ ";\u20D6;\u20D7;\u20D8" // symbol accents
+ ";\u20D9;\u20DA;\u20DB" // symbol accents
+ ";\u20DC;\u20DD;\u20DE" // symbol accents
+ ";\u20DF;\u20E0;\u20E1" // symbol accents
+ ",'\u002D';\u00AD" // dashes
+ ";\u2010;\u2011;\u2012" // dashes
+ ";\u2013;\u2014;\u2015" // dashes
+ ";\u2212" // dashes
// other punctuation
+ "<'\u005f'" // underline/underscore (spacing)
+ "<\u00af" // overline or macron (spacing)
+ "<'\u002c'" // comma (spacing)
+ "<'\u003b'" // semicolon
+ "<'\u003a'" // colon
+ "<'\u0021'" // exclamation point
+ "<\u00a1" // inverted exclamation point
+ "<'\u003f'" // question mark
+ "<\u00bf" // inverted question mark
+ "<'\u002f'" // slash
+ "<'\u002e'" // period/full stop
+ "<\u00b4" // acute accent (spacing)
+ "<'\u0060'" // grave accent (spacing)
+ "<'\u005e'" // circumflex accent (spacing)
+ "<\u00a8" // diaresis/umlaut accent (spacing)
+ "<'\u007e'" // tilde accent (spacing)
+ "<\u00b7" // middle dot (spacing)
+ "<\u00b8" // cedilla accent (spacing)
+ "<'\u0027'" // apostrophe
+ "<'\"'" // quotation marks
+ "<\u00ab" // left angle quotes
+ "<\u00bb" // right angle quotes
+ "<'\u0028'" // left parenthesis
+ "<'\u0029'" // right parenthesis
+ "<'\u005b'" // left bracket
+ "<'\u005d'" // right bracket
+ "<'\u007b'" // left brace
+ "<'\u007d'" // right brace
+ "<\u00a7" // section symbol
+ "<\u00b6" // paragraph symbol
+ "<\u00a9" // copyright symbol
+ "<\u00ae" // registered trademark symbol
+ "<'\u0040'" // at sign
+ "<\u00a4" // international currency symbol
+ "<\u0e3f" // baht sign
+ "<\u00a2" // cent sign
+ "<\u20a1" // colon sign
+ "<\u20a2" // cruzeiro sign
+ "<'\u0024'" // dollar sign
+ "<\u20ab" // dong sign
+ "<\u20ac" // euro sign
+ "<\u20a3" // franc sign
+ "<\u20a4" // lira sign
+ "<\u20a5" // mill sign
+ "<\u20a6" // naira sign
+ "<\u20a7" // peseta sign
+ "<\u00a3" // pound-sterling sign
+ "<\u20a8" // rupee sign
+ "<\u20aa" // new shekel sign
+ "<\u20a9" // won sign
+ "<\u00a5" // yen sign
+ "<'\u002a'" // asterisk
+ "<'\\'" // backslash
+ "<'\u0026'" // ampersand
+ "<'\u0023'" // number sign
+ "<'\u0025'" // percent sign
+ "<'\u002b'" // plus sign
+ "<\u00b1" // plus-or-minus sign
+ "<\u00f7" // divide sign
+ "<\u00d7" // multiply sign
+ "<'\u003c'" // less-than sign
+ "<'\u003d'" // equal sign
+ "<'\u003e'" // greater-than sign
+ "<\u00ac" // end of line symbol/logical NOT symbol
+ "<'\u007c'" // vertical line/logical OR symbol
+ "<\u00a6" // broken vertical line
+ "<\u00b0" // degree symbol
+ "<\u00b5" // micro symbol
// NUMERICS
+ "<0<1<2<3<4<5<6<7<8<9"
+ "<\u00bc<\u00bd<\u00be" // 1/4,1/2,3/4 fractions
// NON-IGNORABLES
+ "<a,A"
+ "<b,B"
+ "<c,C"
+ "<d,D"
+ "<\u00F0,\u00D0" // eth
+ "<e,E"
+ "<f,F"
+ "<g,G"
+ "<h,H"
+ "<i,I"
+ "<j,J"
+ "<k,K"
+ "<l,L"
+ "<m,M"
+ "<n,N"
+ "<o,O"
+ "<p,P"
+ "<q,Q"
+ "<r,R"
+ "<s, S & SS,\u00DF" // s-zet
+ "<t,T"
+ "& TH, \u00DE &TH, \u00FE " // thorn
+ "<u,U"
+ "<v,V"
+ "<w,W"
+ "<x,X"
+ "<y,Y"
+ "<z,Z"
+ "&AE,\u00C6" // ae & AE ligature
+ "&AE,\u00E6"
+ "&OE,\u0152" // oe & OE ligature
+ "&OE,\u0153";
// No instantiation
private CollationRules() {
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 1999, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.util.locale.provider;
import java.text.Collator;
import java.text.ParseException;
import java.text.RuleBasedCollator;
import java.text.spi.CollatorProvider;
import java.util.Locale;
import java.util.Set;
/**
* Concrete implementation of the
* {@link java.text.spi.CollatorProvider CollatorProvider} class
* for the JRE LocaleProviderAdapter.
*/
public class CollatorProviderImpl extends CollatorProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CollatorProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>Collator</code> instance for the specified locale.
* @param locale the desired locale.
* @return the <code>Collator</code> for the desired locale.
* @exception NullPointerException if
* <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.Collator#getInstance(java.util.Locale)
*/
@Override
public Collator getInstance(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
Collator result = null;
// Load the resource of the desired locale from resource
// manager.
String colString = LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCollationData();
try
{
result = new RuleBasedCollator(CollationRules.DEFAULTRULES +
colString);
}
catch(ParseException foo)
{
// predefined tables should contain correct grammar
try {
result = new RuleBasedCollator(CollationRules.DEFAULTRULES);
} catch (ParseException bar) {
// the default rules should always be parsable.
throw new InternalError(bar);
}
}
// Now that RuleBasedCollator adds expansions for pre-composed characters
// into their decomposed equivalents, the default collators don't need
// to have decomposition turned on. Laura, 5/5/98, bug 4114077
result.setDecomposition(Collator.NO_DECOMPOSITION);
return (Collator)result.clone();
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.Locale;
import java.util.Set;
import java.util.spi.CurrencyNameProvider;
/**
* Concrete implementation of the
* {@link java.util.spi.CurrencyNameProvider CurrencyNameProvider} class
* for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class CurrencyNameProviderImpl extends CurrencyNameProvider
implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CurrencyNameProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
/**
* Gets the symbol of the given currency code for the specified locale.
* For example, for "USD" (US Dollar), the symbol is "$" if the specified
* locale is the US, while for other locales it may be "US$". If no
* symbol can be determined, null should be returned.
*
* @param currencyCode the ISO 4217 currency code, which
* consists of three upper-case letters between 'A' (U+0041) and
* 'Z' (U+005A)
* @param locale the desired locale
* @return the symbol of the given currency code for the specified locale, or null if
* the symbol is not available for the locale
* @exception NullPointerException if <code>currencyCode</code> or
* <code>locale</code> is null
* @exception IllegalArgumentException if <code>currencyCode</code> is not in
* the form of three upper-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Currency#getSymbol(java.util.Locale)
*/
@Override
public String getSymbol(String currencyCode, Locale locale) {
return getString(currencyCode.toUpperCase(Locale.ROOT), locale);
}
/**
* Returns a name for the currency that is appropriate for display to the
* user. The default implementation returns null.
*
* @param currencyCode the ISO 4217 currency code, which
* consists of three upper-case letters between 'A' (U+0041) and
* 'Z' (U+005A)
* @param locale the desired locale
* @return the name for the currency that is appropriate for display to the
* user, or null if the name is not available for the locale
* @exception IllegalArgumentException if <code>currencyCode</code> is not in
* the form of three upper-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>currencyCode</code> or
* <code>locale</code> is <code>null</code>
* @since 1.7
*/
@Override
public String getDisplayName(String currencyCode, Locale locale) {
return getString(currencyCode.toLowerCase(Locale.ROOT), locale);
}
private String getString(String key, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCurrencyName(key);
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 1999, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.spi.DateFormatProvider;
import java.util.Calendar;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.DateFormatProvider
* DateFormatProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class DateFormatProviderImpl extends DateFormatProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public DateFormatProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>DateFormat</code> instance which formats time
* with the given formatting style for the specified locale.
* @param style the given formatting style. Either one of
* {@link java.text.DateFormat#SHORT DateFormat.SHORT},
* {@link java.text.DateFormat#MEDIUM DateFormat.MEDIUM},
* {@link java.text.DateFormat#LONG DateFormat.LONG}, or
* {@link java.text.DateFormat#FULL DateFormat.FULL}.
* @param locale the desired locale.
* @exception IllegalArgumentException if <code>style</code> is invalid,
* or if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>locale</code> is null
* @return a time formatter.
* @see java.text.DateFormat#getTimeInstance(int, java.util.Locale)
*/
@Override
public DateFormat getTimeInstance(int style, Locale locale) {
return getInstance(-1, style, locale);
}
/**
* Returns a new <code>DateFormat</code> instance which formats date
* with the given formatting style for the specified locale.
* @param style the given formatting style. Either one of
* {@link java.text.DateFormat#SHORT DateFormat.SHORT},
* {@link java.text.DateFormat#MEDIUM DateFormat.MEDIUM},
* {@link java.text.DateFormat#LONG DateFormat.LONG}, or
* {@link java.text.DateFormat#FULL DateFormat.FULL}.
* @param locale the desired locale.
* @exception IllegalArgumentException if <code>style</code> is invalid,
* or if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>locale</code> is null
* @return a date formatter.
* @see java.text.DateFormat#getDateInstance(int, java.util.Locale)
*/
@Override
public DateFormat getDateInstance(int style, Locale locale) {
return getInstance(style, -1, locale);
}
/**
* Returns a new <code>DateFormat</code> instance which formats date and time
* with the given formatting style for the specified locale.
* @param dateStyle the given date formatting style. Either one of
* {@link java.text.DateFormat#SHORT DateFormat.SHORT},
* {@link java.text.DateFormat#MEDIUM DateFormat.MEDIUM},
* {@link java.text.DateFormat#LONG DateFormat.LONG}, or
* {@link java.text.DateFormat#FULL DateFormat.FULL}.
* @param timeStyle the given time formatting style. Either one of
* {@link java.text.DateFormat#SHORT DateFormat.SHORT},
* {@link java.text.DateFormat#MEDIUM DateFormat.MEDIUM},
* {@link java.text.DateFormat#LONG DateFormat.LONG}, or
* {@link java.text.DateFormat#FULL DateFormat.FULL}.
* @param locale the desired locale.
* @exception IllegalArgumentException if <code>dateStyle</code> or
* <code>timeStyle</code> is invalid,
* or if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>locale</code> is null
* @return a date/time formatter.
* @see java.text.DateFormat#getDateTimeInstance(int, int, java.util.Locale)
*/
@Override
public DateFormat getDateTimeInstance(int dateStyle, int timeStyle,
Locale locale) {
return getInstance(dateStyle, timeStyle, locale);
}
private DateFormat getInstance(int dateStyle, int timeStyle, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
SimpleDateFormat sdf = new SimpleDateFormat("", locale);
Calendar cal = sdf.getCalendar();
try {
String pattern = LocaleProviderAdapter.forType(type)
.getLocaleResources(locale).getDateTimePattern(timeStyle, dateStyle,
cal);
sdf.applyPattern(pattern);
} catch (MissingResourceException mre) {
// Specify the fallback pattern
sdf.applyPattern("M/d/yy h:mm a");
}
return sdf;
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 1999, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.text.DateFormatSymbols;
import java.text.spi.DateFormatSymbolsProvider;
import java.util.Locale;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.DateFormatSymbolsProvider
* DateFormatSymbolsProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class DateFormatSymbolsProviderImpl extends DateFormatSymbolsProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public DateFormatSymbolsProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>DateFormatSymbols</code> instance for the
* specified locale.
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a <code>DateFormatSymbols</code> instance.
* @see java.text.DateFormatSymbols#getInstance(java.util.Locale)
*/
@Override
public DateFormatSymbols getInstance(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
return new DateFormatSymbols(locale);
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 1999, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.text.DecimalFormatSymbols;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.util.Locale;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.DecimalFormatSymbolsProvider
* DecimalFormatSymbolsProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class DecimalFormatSymbolsProviderImpl extends DecimalFormatSymbolsProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public DecimalFormatSymbolsProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>DecimalFormatSymbols</code> instance for the
* specified locale.
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a <code>DecimalFormatSymbols</code> instance.
* @see java.text.DecimalFormatSymbols#getInstance(java.util.Locale)
*/
@Override
public DecimalFormatSymbols getInstance(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
return new DecimalFormatSymbols(locale);
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,525 @@
/*
* Copyright (c) 1999, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.util.locale.provider;
import java.io.IOException;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* A subclass of RuleBasedBreakIterator that adds the ability to use a dictionary
* to further subdivide ranges of text beyond what is possible using just the
* state-table-based algorithm. This is necessary, for example, to handle
* word and line breaking in Thai, which doesn't use spaces between words. The
* state-table-based algorithm used by RuleBasedBreakIterator is used to divide
* up text as far as possible, and then contiguous ranges of letters are
* repeatedly compared against a list of known words (i.e., the dictionary)
* to divide them up into words.
*
* DictionaryBasedBreakIterator uses the same rule language as RuleBasedBreakIterator,
* but adds one more special substitution name: &lt;dictionary&gt;. This substitution
* name is used to identify characters in words in the dictionary. The idea is that
* if the iterator passes over a chunk of text that includes two or more characters
* in a row that are included in &lt;dictionary&gt;, it goes back through that range and
* derives additional break positions (if possible) using the dictionary.
*
* DictionaryBasedBreakIterator is also constructed with the filename of a dictionary
* file. It follows a prescribed search path to locate the dictionary (right now,
* it looks for it in /com/ibm/text/resources in each directory in the classpath,
* and won't find it in JAR files, but this location is likely to change). The
* dictionary file is in a serialized binary format. We have a very primitive (and
* slow) BuildDictionaryFile utility for creating dictionary files, but aren't
* currently making it public. Contact us for help.
*/
class DictionaryBasedBreakIterator extends RuleBasedBreakIterator {
/**
* a list of known words that is used to divide up contiguous ranges of letters,
* stored in a compressed, indexed, format that offers fast access
*/
private BreakDictionary dictionary;
/**
* a list of flags indicating which character categories are contained in
* the dictionary file (this is used to determine which ranges of characters
* to apply the dictionary to)
*/
private boolean[] categoryFlags;
/**
* a temporary hiding place for the number of dictionary characters in the
* last range passed over by next()
*/
private int dictionaryCharCount;
/**
* when a range of characters is divided up using the dictionary, the break
* positions that are discovered are stored here, preventing us from having
* to use either the dictionary or the state table again until the iterator
* leaves this range of text
*/
private int[] cachedBreakPositions;
/**
* if cachedBreakPositions is not null, this indicates which item in the
* cache the current iteration position refers to
*/
private int positionInCache;
/**
* Constructs a DictionaryBasedBreakIterator.
* @param description Same as the description parameter on RuleBasedBreakIterator,
* except for the special meaning of "<dictionary>". This parameter is just
* passed through to RuleBasedBreakIterator's constructor.
* @param dictionaryFilename The filename of the dictionary file to use
*/
DictionaryBasedBreakIterator(String dataFile, String dictionaryFile)
throws IOException {
super(dataFile);
byte[] tmp = super.getAdditionalData();
if (tmp != null) {
prepareCategoryFlags(tmp);
super.setAdditionalData(null);
}
dictionary = new BreakDictionary(dictionaryFile);
}
private void prepareCategoryFlags(byte[] data) {
categoryFlags = new boolean[data.length];
for (int i = 0; i < data.length; i++) {
categoryFlags[i] = (data[i] == (byte)1) ? true : false;
}
}
@Override
public void setText(CharacterIterator newText) {
super.setText(newText);
cachedBreakPositions = null;
dictionaryCharCount = 0;
positionInCache = 0;
}
/**
* Sets the current iteration position to the beginning of the text.
* (i.e., the CharacterIterator's starting offset).
* @return The offset of the beginning of the text.
*/
@Override
public int first() {
cachedBreakPositions = null;
dictionaryCharCount = 0;
positionInCache = 0;
return super.first();
}
/**
* Sets the current iteration position to the end of the text.
* (i.e., the CharacterIterator's ending offset).
* @return The text's past-the-end offset.
*/
@Override
public int last() {
cachedBreakPositions = null;
dictionaryCharCount = 0;
positionInCache = 0;
return super.last();
}
/**
* Advances the iterator one step backwards.
* @return The position of the last boundary position before the
* current iteration position
*/
@Override
public int previous() {
CharacterIterator text = getText();
// if we have cached break positions and we're still in the range
// covered by them, just move one step backward in the cache
if (cachedBreakPositions != null && positionInCache > 0) {
--positionInCache;
text.setIndex(cachedBreakPositions[positionInCache]);
return cachedBreakPositions[positionInCache];
}
// otherwise, dump the cache and use the inherited previous() method to move
// backward. This may fill up the cache with new break positions, in which
// case we have to mark our position in the cache
else {
cachedBreakPositions = null;
int result = super.previous();
if (cachedBreakPositions != null) {
positionInCache = cachedBreakPositions.length - 2;
}
return result;
}
}
/**
* Sets the current iteration position to the last boundary position
* before the specified position.
* @param offset The position to begin searching from
* @return The position of the last boundary before "offset"
*/
@Override
public int preceding(int offset) {
CharacterIterator text = getText();
checkOffset(offset, text);
// if we have no cached break positions, or "offset" is outside the
// range covered by the cache, we can just call the inherited routine
// (which will eventually call other routines in this class that may
// refresh the cache)
if (cachedBreakPositions == null || offset <= cachedBreakPositions[0] ||
offset > cachedBreakPositions[cachedBreakPositions.length - 1]) {
cachedBreakPositions = null;
return super.preceding(offset);
}
// on the other hand, if "offset" is within the range covered by the cache,
// then all we have to do is search the cache for the last break position
// before "offset"
else {
positionInCache = 0;
while (positionInCache < cachedBreakPositions.length
&& offset > cachedBreakPositions[positionInCache]) {
++positionInCache;
}
--positionInCache;
text.setIndex(cachedBreakPositions[positionInCache]);
return text.getIndex();
}
}
/**
* Sets the current iteration position to the first boundary position after
* the specified position.
* @param offset The position to begin searching forward from
* @return The position of the first boundary after "offset"
*/
@Override
public int following(int offset) {
CharacterIterator text = getText();
checkOffset(offset, text);
// if we have no cached break positions, or if "offset" is outside the
// range covered by the cache, then dump the cache and call our
// inherited following() method. This will call other methods in this
// class that may refresh the cache.
if (cachedBreakPositions == null || offset < cachedBreakPositions[0] ||
offset >= cachedBreakPositions[cachedBreakPositions.length - 1]) {
cachedBreakPositions = null;
return super.following(offset);
}
// on the other hand, if "offset" is within the range covered by the
// cache, then just search the cache for the first break position
// after "offset"
else {
positionInCache = 0;
while (positionInCache < cachedBreakPositions.length
&& offset >= cachedBreakPositions[positionInCache]) {
++positionInCache;
}
text.setIndex(cachedBreakPositions[positionInCache]);
return text.getIndex();
}
}
/**
* This is the implementation function for next().
*/
@Override
protected int handleNext() {
CharacterIterator text = getText();
// if there are no cached break positions, or if we've just moved
// off the end of the range covered by the cache, we have to dump
// and possibly regenerate the cache
if (cachedBreakPositions == null ||
positionInCache == cachedBreakPositions.length - 1) {
// start by using the inherited handleNext() to find a tentative return
// value. dictionaryCharCount tells us how many dictionary characters
// we passed over on our way to the tentative return value
int startPos = text.getIndex();
dictionaryCharCount = 0;
int result = super.handleNext();
// if we passed over more than one dictionary character, then we use
// divideUpDictionaryRange() to regenerate the cached break positions
// for the new range
if (dictionaryCharCount > 1 && result - startPos > 1) {
divideUpDictionaryRange(startPos, result);
}
// otherwise, the value we got back from the inherited fuction
// is our return value, and we can dump the cache
else {
cachedBreakPositions = null;
return result;
}
}
// if the cache of break positions has been regenerated (or existed all
// along), then just advance to the next break position in the cache
// and return it
if (cachedBreakPositions != null) {
++positionInCache;
text.setIndex(cachedBreakPositions[positionInCache]);
return cachedBreakPositions[positionInCache];
}
return -9999; // SHOULD NEVER GET HERE!
}
/**
* Looks up a character category for a character.
*/
@Override
protected int lookupCategory(int c) {
// this override of lookupCategory() exists only to keep track of whether we've
// passed over any dictionary characters. It calls the inherited lookupCategory()
// to do the real work, and then checks whether its return value is one of the
// categories represented in the dictionary. If it is, bump the dictionary-
// character count.
int result = super.lookupCategory(c);
if (result != RuleBasedBreakIterator.IGNORE && categoryFlags[result]) {
++dictionaryCharCount;
}
return result;
}
/**
* This is the function that actually implements the dictionary-based
* algorithm. Given the endpoints of a range of text, it uses the
* dictionary to determine the positions of any boundaries in this
* range. It stores all the boundary positions it discovers in
* cachedBreakPositions so that we only have to do this work once
* for each time we enter the range.
*/
@SuppressWarnings("unchecked")
private void divideUpDictionaryRange(int startPos, int endPos) {
CharacterIterator text = getText();
// the range we're dividing may begin or end with non-dictionary characters
// (i.e., for line breaking, we may have leading or trailing punctuation
// that needs to be kept with the word). Seek from the beginning of the
// range to the first dictionary character
text.setIndex(startPos);
int c = getCurrent();
int category = lookupCategory(c);
while (category == IGNORE || !categoryFlags[category]) {
c = getNext();
category = lookupCategory(c);
}
// initialize. We maintain two stacks: currentBreakPositions contains
// the list of break positions that will be returned if we successfully
// finish traversing the whole range now. possibleBreakPositions lists
// all other possible word ends we've passed along the way. (Whenever
// we reach an error [a sequence of characters that can't begin any word
// in the dictionary], we back up, possibly delete some breaks from
// currentBreakPositions, move a break from possibleBreakPositions
// to currentBreakPositions, and start over from there. This process
// continues in this way until we either successfully make it all the way
// across the range, or exhaust all of our combinations of break
// positions.)
Stack<Integer> currentBreakPositions = new Stack<>();
Stack<Integer> possibleBreakPositions = new Stack<>();
List<Integer> wrongBreakPositions = new ArrayList<>();
// the dictionary is implemented as a trie, which is treated as a state
// machine. -1 represents the end of a legal word. Every word in the
// dictionary is represented by a path from the root node to -1. A path
// that ends in state 0 is an illegal combination of characters.
int state = 0;
// these two variables are used for error handling. We keep track of the
// farthest we've gotten through the range being divided, and the combination
// of breaks that got us that far. If we use up all possible break
// combinations, the text contains an error or a word that's not in the
// dictionary. In this case, we "bless" the break positions that got us the
// farthest as real break positions, and then start over from scratch with
// the character where the error occurred.
int farthestEndPoint = text.getIndex();
Stack<Integer> bestBreakPositions = null;
// initialize (we always exit the loop with a break statement)
c = getCurrent();
while (true) {
// if we can transition to state "-1" from our current state, we're
// on the last character of a legal word. Push that position onto
// the possible-break-positions stack
if (dictionary.getNextState(state, 0) == -1) {
possibleBreakPositions.push(text.getIndex());
}
// look up the new state to transition to in the dictionary
state = dictionary.getNextStateFromCharacter(state, c);
// if the character we're sitting on causes us to transition to
// the "end of word" state, then it was a non-dictionary character
// and we've successfully traversed the whole range. Drop out
// of the loop.
if (state == -1) {
currentBreakPositions.push(text.getIndex());
break;
}
// if the character we're sitting on causes us to transition to
// the error state, or if we've gone off the end of the range
// without transitioning to the "end of word" state, we've hit
// an error...
else if (state == 0 || text.getIndex() >= endPos) {
// if this is the farthest we've gotten, take note of it in
// case there's an error in the text
if (text.getIndex() > farthestEndPoint) {
farthestEndPoint = text.getIndex();
@SuppressWarnings("unchecked")
Stack<Integer> currentBreakPositionsCopy = (Stack<Integer>) currentBreakPositions.clone();
bestBreakPositions = currentBreakPositionsCopy;
}
// wrongBreakPositions is a list of all break positions
// we've tried starting that didn't allow us to traverse
// all the way through the text. Every time we pop a
// break position off of currentBreakPositions, we put it
// into wrongBreakPositions to avoid trying it again later.
// If we make it to this spot, we're either going to back
// up to a break in possibleBreakPositions and try starting
// over from there, or we've exhausted all possible break
// positions and are going to do the fallback procedure.
// This loop prevents us from messing with anything in
// possibleBreakPositions that didn't work as a starting
// point the last time we tried it (this is to prevent a bunch of
// repetitive checks from slowing down some extreme cases)
while (!possibleBreakPositions.isEmpty()
&& wrongBreakPositions.contains(possibleBreakPositions.peek())) {
possibleBreakPositions.pop();
}
// if we've used up all possible break-position combinations, there's
// an error or an unknown word in the text. In this case, we start
// over, treating the farthest character we've reached as the beginning
// of the range, and "blessing" the break positions that got us that
// far as real break positions
if (possibleBreakPositions.isEmpty()) {
if (bestBreakPositions != null) {
currentBreakPositions = bestBreakPositions;
if (farthestEndPoint < endPos) {
text.setIndex(farthestEndPoint + 1);
}
else {
break;
}
}
else {
if ((currentBreakPositions.size() == 0 ||
currentBreakPositions.peek().intValue() != text.getIndex())
&& text.getIndex() != startPos) {
currentBreakPositions.push(new Integer(text.getIndex()));
}
getNext();
currentBreakPositions.push(new Integer(text.getIndex()));
}
}
// if we still have more break positions we can try, then promote the
// last break in possibleBreakPositions into currentBreakPositions,
// and get rid of all entries in currentBreakPositions that come after
// it. Then back up to that position and start over from there (i.e.,
// treat that position as the beginning of a new word)
else {
Integer temp = possibleBreakPositions.pop();
Integer temp2 = null;
while (!currentBreakPositions.isEmpty() && temp.intValue() <
currentBreakPositions.peek().intValue()) {
temp2 = currentBreakPositions.pop();
wrongBreakPositions.add(temp2);
}
currentBreakPositions.push(temp);
text.setIndex(currentBreakPositions.peek().intValue());
}
// re-sync "c" for the next go-round, and drop out of the loop if
// we've made it off the end of the range
c = getCurrent();
if (text.getIndex() >= endPos) {
break;
}
}
// if we didn't hit any exceptional conditions on this last iteration,
// just advance to the next character and loop
else {
c = getNext();
}
}
// dump the last break position in the list, and replace it with the actual
// end of the range (which may be the same character, or may be further on
// because the range actually ended with non-dictionary characters we want to
// keep with the word)
if (!currentBreakPositions.isEmpty()) {
currentBreakPositions.pop();
}
currentBreakPositions.push(endPos);
// create a regular array to hold the break positions and copy
// the break positions from the stack to the array (in addition,
// our starting position goes into this array as a break position).
// This array becomes the cache of break positions used by next()
// and previous(), so this is where we actually refresh the cache.
cachedBreakPositions = new int[currentBreakPositions.size() + 1];
cachedBreakPositions[0] = startPos;
for (int i = 0; i < currentBreakPositions.size(); i++) {
cachedBreakPositions[i + 1] = currentBreakPositions.elementAt(i).intValue();
}
positionInCache = 0;
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
/**
* FallbackProviderAdapter implementation.
*
* @author Naoto Sato
*/
public class FallbackLocaleProviderAdapter extends JRELocaleProviderAdapter {
/**
* Supported language tag set.
*/
private static final Set<String> rootTagSet =
Collections.singleton(Locale.ROOT.toLanguageTag());
/**
* Fallback provider only provides the ROOT locale data.
*/
private final LocaleResources rootLocaleResources =
new LocaleResources(this, Locale.ROOT);
/**
* Returns the type of this LocaleProviderAdapter
*/
@Override
public LocaleProviderAdapter.Type getAdapterType() {
return Type.FALLBACK;
}
@Override
public LocaleResources getLocaleResources(Locale locale) {
return rootLocaleResources;
}
@Override
protected Set<String> createLanguageTagSet(String category) {
return rootTagSet;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.spi.LocaleServiceProvider;
/**
* LocaleProviderAdapter implementation for the host locale data.
* Currently it is only implemented on Windows Vista or later.
*
* @author Naoto Sato
*/
public class HostLocaleProviderAdapter extends AuxLocaleProviderAdapter {
/**
* Returns the type of this LocaleProviderAdapter
*/
@Override
public LocaleProviderAdapter.Type getAdapterType() {
return LocaleProviderAdapter.Type.HOST;
}
@Override
@SuppressWarnings("unchecked")
protected <P extends LocaleServiceProvider> P findInstalledProvider(final Class<P> c) {
try {
Method getter = HostLocaleProviderAdapterImpl.class.getMethod(
"get" + c.getSimpleName(), (Class<?>[]) null);
return (P)getter.invoke(null, (Object[]) null);
} catch (NoSuchMethodException |
IllegalAccessException |
IllegalArgumentException |
InvocationTargetException ex) {
LocaleServiceProviderPool.config(HostLocaleProviderAdapter.class, ex.toString());
}
return null;
}
}

View File

@@ -0,0 +1,645 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.lang.ref.SoftReference;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Calendar;
import java.util.Collections;
import java.util.Currency;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle.Control;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import sun.util.spi.CalendarProvider;
/**
* LocaleProviderdapter implementation for the Windows locale data.
*
* @author Naoto Sato
*/
public class HostLocaleProviderAdapterImpl {
// locale categories
private static final int CAT_DISPLAY = 0;
private static final int CAT_FORMAT = 1;
// NumberFormat styles
private static final int NF_NUMBER = 0;
private static final int NF_CURRENCY = 1;
private static final int NF_PERCENT = 2;
private static final int NF_INTEGER = 3;
private static final int NF_MAX = NF_INTEGER;
// CalendarData value types
private static final int CD_FIRSTDAYOFWEEK = 0;
private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
// Currency/Locale display name types
private static final int DN_CURRENCY_NAME = 0;
private static final int DN_CURRENCY_SYMBOL = 1;
private static final int DN_LOCALE_LANGUAGE = 2;
private static final int DN_LOCALE_SCRIPT = 3;
private static final int DN_LOCALE_REGION = 4;
private static final int DN_LOCALE_VARIANT = 5;
// Native Calendar ID to LDML calendar type map
private static final String[] calIDToLDML = {
"",
"gregory",
"gregory_en-US",
"japanese",
"roc",
"", // No appropriate type for CAL_KOREA
"islamic",
"buddhist",
"hebrew",
"gregory_fr",
"gregory_ar",
"gregory_en",
"gregory_fr",
};
// Caches
private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatCache = new ConcurrentHashMap<>();
private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsCache = new ConcurrentHashMap<>();
private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatCache = new ConcurrentHashMap<>();
private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsCache = new ConcurrentHashMap<>();
private static final Set<Locale> supportedLocaleSet;
private static final String nativeDisplayLanguage;
static {
Set<Locale> tmpSet = new HashSet<>();
if (initialize()) {
// Assuming the default locales do not include any extensions, so
// no stripping is needed here.
Control c = Control.getNoFallbackControl(Control.FORMAT_DEFAULT);
String displayLocale = getDefaultLocale(CAT_DISPLAY);
Locale l = Locale.forLanguageTag(displayLocale.replace('_', '-'));
tmpSet.addAll(c.getCandidateLocales("", l));
nativeDisplayLanguage = l.getLanguage();
String formatLocale = getDefaultLocale(CAT_FORMAT);
if (!formatLocale.equals(displayLocale)) {
l = Locale.forLanguageTag(formatLocale.replace('_', '-'));
tmpSet.addAll(c.getCandidateLocales("", l));
}
} else {
nativeDisplayLanguage = "";
}
supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
}
private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
public static DateFormatProvider getDateFormatProvider() {
return new DateFormatProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedCalendarLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedCalendarLocale(locale);
}
@Override
public DateFormat getDateInstance(int style, Locale locale) {
AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
return new SimpleDateFormat(patterns.get(style/2),
getCalendarLocale(locale));
}
@Override
public DateFormat getTimeInstance(int style, Locale locale) {
AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
return new SimpleDateFormat(patterns.get(style/2+2),
getCalendarLocale(locale));
}
@Override
public DateFormat getDateTimeInstance(int dateStyle,
int timeStyle, Locale locale) {
AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
String pattern = new StringBuilder(patterns.get(dateStyle/2))
.append(" ")
.append(patterns.get(timeStyle/2+2))
.toString();
return new SimpleDateFormat(pattern, getCalendarLocale(locale));
}
private AtomicReferenceArray<String> getDateTimePatterns(Locale locale) {
AtomicReferenceArray<String> patterns;
SoftReference<AtomicReferenceArray<String>> ref = dateFormatCache.get(locale);
if (ref == null || (patterns = ref.get()) == null) {
String langtag = removeExtensions(locale).toLanguageTag();
patterns = new AtomicReferenceArray<>(4);
patterns.compareAndSet(0, null, convertDateTimePattern(
getDateTimePattern(DateFormat.LONG, -1, langtag)));
patterns.compareAndSet(1, null, convertDateTimePattern(
getDateTimePattern(DateFormat.SHORT, -1, langtag)));
patterns.compareAndSet(2, null, convertDateTimePattern(
getDateTimePattern(-1, DateFormat.LONG, langtag)));
patterns.compareAndSet(3, null, convertDateTimePattern(
getDateTimePattern(-1, DateFormat.SHORT, langtag)));
ref = new SoftReference<>(patterns);
dateFormatCache.put(locale, ref);
}
return patterns;
}
};
}
public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
return new DateFormatSymbolsProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedCalendarLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedCalendarLocale(locale);
}
@Override
public DateFormatSymbols getInstance(Locale locale) {
DateFormatSymbols dfs;
SoftReference<DateFormatSymbols> ref =
dateFormatSymbolsCache.get(locale);
if (ref == null || (dfs = ref.get()) == null) {
dfs = new DateFormatSymbols(locale);
String langTag = removeExtensions(locale).toLanguageTag();
dfs.setAmPmStrings(getAmPmStrings(langTag, dfs.getAmPmStrings()));
dfs.setEras(getEras(langTag, dfs.getEras()));
dfs.setMonths(getMonths(langTag, dfs.getMonths()));
dfs.setShortMonths(getShortMonths(langTag, dfs.getShortMonths()));
dfs.setWeekdays(getWeekdays(langTag, dfs.getWeekdays()));
dfs.setShortWeekdays(getShortWeekdays(langTag, dfs.getShortWeekdays()));
ref = new SoftReference<>(dfs);
dateFormatSymbolsCache.put(locale, ref);
}
return (DateFormatSymbols)dfs.clone();
}
};
}
public static NumberFormatProvider getNumberFormatProvider() {
return new NumberFormatProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedNativeDigitLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedNativeDigitLocale(locale);
}
@Override
public NumberFormat getCurrencyInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
return new DecimalFormat(patterns.get(NF_CURRENCY),
DecimalFormatSymbols.getInstance(locale));
}
@Override
public NumberFormat getIntegerInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
return new DecimalFormat(patterns.get(NF_INTEGER),
DecimalFormatSymbols.getInstance(locale));
}
@Override
public NumberFormat getNumberInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
return new DecimalFormat(patterns.get(NF_NUMBER),
DecimalFormatSymbols.getInstance(locale));
}
@Override
public NumberFormat getPercentInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
return new DecimalFormat(patterns.get(NF_PERCENT),
DecimalFormatSymbols.getInstance(locale));
}
private AtomicReferenceArray<String> getNumberPatterns(Locale locale) {
AtomicReferenceArray<String> patterns;
SoftReference<AtomicReferenceArray<String>> ref = numberFormatCache.get(locale);
if (ref == null || (patterns = ref.get()) == null) {
String langtag = locale.toLanguageTag();
patterns = new AtomicReferenceArray<>(NF_MAX+1);
for (int i = 0; i <= NF_MAX; i++) {
patterns.compareAndSet(i, null, getNumberPattern(i, langtag));
}
ref = new SoftReference<>(patterns);
numberFormatCache.put(locale, ref);
}
return patterns;
}
};
}
public static DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
return new DecimalFormatSymbolsProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedNativeDigitLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedNativeDigitLocale(locale);
}
@Override
public DecimalFormatSymbols getInstance(Locale locale) {
DecimalFormatSymbols dfs;
SoftReference<DecimalFormatSymbols> ref =
decimalFormatSymbolsCache.get(locale);
if (ref == null || (dfs = ref.get()) == null) {
dfs = new DecimalFormatSymbols(getNumberLocale(locale));
String langTag = removeExtensions(locale).toLanguageTag();
// DecimalFormatSymbols.setInternationalCurrencySymbol() has
// a side effect of setting the currency symbol as well. So
// the calling order is relevant here.
dfs.setInternationalCurrencySymbol(getInternationalCurrencySymbol(langTag, dfs.getInternationalCurrencySymbol()));
dfs.setCurrencySymbol(getCurrencySymbol(langTag, dfs.getCurrencySymbol()));
dfs.setDecimalSeparator(getDecimalSeparator(langTag, dfs.getDecimalSeparator()));
dfs.setGroupingSeparator(getGroupingSeparator(langTag, dfs.getGroupingSeparator()));
dfs.setInfinity(getInfinity(langTag, dfs.getInfinity()));
dfs.setMinusSign(getMinusSign(langTag, dfs.getMinusSign()));
dfs.setMonetaryDecimalSeparator(getMonetaryDecimalSeparator(langTag, dfs.getMonetaryDecimalSeparator()));
dfs.setNaN(getNaN(langTag, dfs.getNaN()));
dfs.setPercent(getPercent(langTag, dfs.getPercent()));
dfs.setPerMill(getPerMill(langTag, dfs.getPerMill()));
dfs.setZeroDigit(getZeroDigit(langTag, dfs.getZeroDigit()));
ref = new SoftReference<>(dfs);
decimalFormatSymbolsCache.put(locale, ref);
}
return (DecimalFormatSymbols)dfs.clone();
}
};
}
public static CalendarDataProvider getCalendarDataProvider() {
return new CalendarDataProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedCalendarLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedCalendarLocale(locale);
}
@Override
public int getFirstDayOfWeek(Locale locale) {
int first = getCalendarDataValue(
removeExtensions(locale).toLanguageTag(),
CD_FIRSTDAYOFWEEK);
if (first != -1) {
return (first + 1) % 7 + 1;
} else {
return 0;
}
}
@Override
public int getMinimalDaysInFirstWeek(Locale locale) {
return 0;
}
};
}
public static CalendarProvider getCalendarProvider() {
return new CalendarProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedCalendarLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedCalendarLocale(locale);
}
@Override
public Calendar getInstance(TimeZone zone, Locale locale) {
return new Calendar.Builder()
.setLocale(getCalendarLocale(locale))
.setTimeZone(zone)
.setInstant(System.currentTimeMillis())
.build();
}
};
}
public static CurrencyNameProvider getCurrencyNameProvider() {
return new CurrencyNameProvider() {
@Override
public Locale[] getAvailableLocales() {
return supportedLocale;
}
@Override
public boolean isSupportedLocale(Locale locale) {
// Ignore the extensions for now
return supportedLocaleSet.contains(locale.stripExtensions()) &&
locale.getLanguage().equals(nativeDisplayLanguage);
}
@Override
public String getSymbol(String currencyCode, Locale locale) {
// Retrieves the currency symbol by calling
// GetLocaleInfoEx(LOCALE_SCURRENCY).
// It only works with the "locale"'s currency in its native
// language.
try {
if (Currency.getInstance(locale).getCurrencyCode()
.equals(currencyCode)) {
return getDisplayString(locale.toLanguageTag(),
DN_CURRENCY_SYMBOL, currencyCode);
}
} catch (IllegalArgumentException iae) {}
return null;
}
@Override
public String getDisplayName(String currencyCode, Locale locale) {
// Retrieves the display name by calling
// GetLocaleInfoEx(LOCALE_SNATIVECURRNAME).
// It only works with the "locale"'s currency in its native
// language.
try {
if (Currency.getInstance(locale).getCurrencyCode()
.equals(currencyCode)) {
return getDisplayString(locale.toLanguageTag(),
DN_CURRENCY_NAME, currencyCode);
}
} catch (IllegalArgumentException iae) {}
return null;
}
};
}
public static LocaleNameProvider getLocaleNameProvider() {
return new LocaleNameProvider() {
@Override
public Locale[] getAvailableLocales() {
return supportedLocale;
}
@Override
public boolean isSupportedLocale(Locale locale) {
return supportedLocaleSet.contains(locale.stripExtensions()) &&
locale.getLanguage().equals(nativeDisplayLanguage);
}
@Override
public String getDisplayLanguage(String languageCode, Locale locale) {
// Retrieves the display language name by calling
// GetLocaleInfoEx(LOCALE_SLOCALIZEDLANGUAGENAME).
return getDisplayString(locale.toLanguageTag(),
DN_LOCALE_LANGUAGE, languageCode);
}
@Override
public String getDisplayCountry(String countryCode, Locale locale) {
// Retrieves the display country name by calling
// GetLocaleInfoEx(LOCALE_SLOCALIZEDCOUNTRYNAME).
return getDisplayString(locale.toLanguageTag(),
DN_LOCALE_REGION, nativeDisplayLanguage+"-"+countryCode);
}
@Override
public String getDisplayScript(String scriptCode, Locale locale) {
return null;
}
@Override
public String getDisplayVariant(String variantCode, Locale locale) {
return null;
}
};
}
private static String convertDateTimePattern(String winPattern) {
String ret = winPattern.replaceAll("dddd", "EEEE");
ret = ret.replaceAll("ddd", "EEE");
ret = ret.replaceAll("tt", "aa");
ret = ret.replaceAll("g", "GG");
return ret;
}
private static Locale[] getSupportedCalendarLocales() {
if (supportedLocale.length != 0 &&
supportedLocaleSet.contains(Locale.JAPAN) &&
isJapaneseCalendar()) {
Locale[] sup = new Locale[supportedLocale.length+1];
sup[0] = JRELocaleConstants.JA_JP_JP;
System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
return sup;
}
return supportedLocale;
}
private static boolean isSupportedCalendarLocale(Locale locale) {
Locale base = locale;
if (base.hasExtensions() || base.getVariant() != "") {
// strip off extensions and variant.
base = new Locale.Builder()
.setLocale(locale)
.clearExtensions()
.build();
}
if (!supportedLocaleSet.contains(base)) {
return false;
}
int calid = getCalendarID(base.toLanguageTag());
if (calid <= 0 || calid >= calIDToLDML.length) {
return false;
}
String requestedCalType = locale.getUnicodeLocaleType("ca");
String nativeCalType = calIDToLDML[calid]
.replaceFirst("_.*", ""); // remove locale part.
if (requestedCalType == null) {
return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
} else {
return requestedCalType.equals(nativeCalType);
}
}
private static Locale[] getSupportedNativeDigitLocales() {
if (supportedLocale.length != 0 &&
supportedLocaleSet.contains(JRELocaleConstants.TH_TH) &&
isNativeDigit("th-TH")) {
Locale[] sup = new Locale[supportedLocale.length+1];
sup[0] = JRELocaleConstants.TH_TH_TH;
System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
return sup;
}
return supportedLocale;
}
private static boolean isSupportedNativeDigitLocale(Locale locale) {
// special case for th_TH_TH
if (JRELocaleConstants.TH_TH_TH.equals(locale)) {
return isNativeDigit("th-TH");
}
String numtype = null;
Locale base = locale;
if (locale.hasExtensions()) {
numtype = locale.getUnicodeLocaleType("nu");
base = locale.stripExtensions();
}
if (supportedLocaleSet.contains(base)) {
// Only supports Latin or Thai (in thai locales) digits.
if (numtype == null || numtype.equals("latn")) {
return true;
} else if (locale.getLanguage().equals("th")) {
return "thai".equals(numtype) &&
isNativeDigit(locale.toLanguageTag());
}
}
return false;
}
private static Locale removeExtensions(Locale src) {
return new Locale.Builder().setLocale(src).clearExtensions().build();
}
private static boolean isJapaneseCalendar() {
return getCalendarID("ja-JP") == 3; // 3: CAL_JAPAN
}
private static Locale getCalendarLocale(Locale locale) {
int calid = getCalendarID(locale.toLanguageTag());
if (calid > 0 && calid < calIDToLDML.length) {
Locale.Builder lb = new Locale.Builder();
String[] caltype = calIDToLDML[calid].split("_");
if (caltype.length > 1) {
lb.setLocale(Locale.forLanguageTag(caltype[1]));
} else {
lb.setLocale(locale);
}
lb.setUnicodeLocaleKeyword("ca", caltype[0]);
return lb.build();
}
return locale;
}
private static Locale getNumberLocale(Locale src) {
if (JRELocaleConstants.TH_TH.equals(src)) {
if (isNativeDigit("th-TH")) {
Locale.Builder lb = new Locale.Builder().setLocale(src);
lb.setUnicodeLocaleKeyword("nu", "thai");
return lb.build();
}
}
return src;
}
// native methods
// initialize
private static native boolean initialize();
private static native String getDefaultLocale(int cat);
// For DateFormatProvider
private static native String getDateTimePattern(int dateStyle, int timeStyle, String langTag);
private static native int getCalendarID(String langTag);
// For DateFormatSymbolsProvider
private static native String[] getAmPmStrings(String langTag, String[] ampm);
private static native String[] getEras(String langTag, String[] eras);
private static native String[] getMonths(String langTag, String[] months);
private static native String[] getShortMonths(String langTag, String[] smonths);
private static native String[] getWeekdays(String langTag, String[] wdays);
private static native String[] getShortWeekdays(String langTag, String[] swdays);
// For NumberFormatProvider
private static native String getNumberPattern(int numberStyle, String langTag);
private static native boolean isNativeDigit(String langTag);
// For DecimalFormatSymbolsProvider
private static native String getCurrencySymbol(String langTag, String currencySymbol);
private static native char getDecimalSeparator(String langTag, char decimalSeparator);
private static native char getGroupingSeparator(String langTag, char groupingSeparator);
private static native String getInfinity(String langTag, String infinity);
private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol);
private static native char getMinusSign(String langTag, char minusSign);
private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator);
private static native String getNaN(String langTag, String nan);
private static native char getPercent(String langTag, char percent);
private static native char getPerMill(String langTag, char perMill);
private static native char getZeroDigit(String langTag, char zeroDigit);
// For CalendarDataProvider
private static native int getCalendarDataValue(String langTag, int type);
// For Locale/CurrencyNameProvider
private static native String getDisplayString(String langTag, int key, String value);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.Locale;
/**
* Singletons for the well-known JRE-specific Locales. (th_TH isn't JRE specific,
* but it's treated as a special Locale because of the Thai Buddhist calendar
* support.)
*
* @author Masayoshi Okutsu
*/
public class JRELocaleConstants {
public static final Locale JA_JP_JP = new Locale("ja", "JP", "JP");
public static final Locale NO_NO_NY = new Locale("no", "NO", "NY");
public static final Locale TH_TH = new Locale("th", "TH");
public static final Locale TH_TH_TH = new Locale("th", "TH", "TH");
private JRELocaleConstants() {
}
}

View File

@@ -0,0 +1,468 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.io.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
import sun.util.resources.LocaleData;
import sun.util.spi.CalendarProvider;
/**
* LocaleProviderAdapter implementation for the legacy JRE locale data.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter {
private static final String LOCALE_DATA_JAR_NAME = "localedata.jar";
private final ConcurrentMap<String, Set<String>> langtagSets
= new ConcurrentHashMap<>();
private final ConcurrentMap<Locale, LocaleResources> localeResourcesMap
= new ConcurrentHashMap<>();
// LocaleData specific to this LocaleProviderAdapter.
private volatile LocaleData localeData;
/**
* Returns the type of this LocaleProviderAdapter
*/
@Override
public LocaleProviderAdapter.Type getAdapterType() {
return Type.JRE;
}
/**
* Getter method for Locale Service Providers
*/
@Override
@SuppressWarnings("unchecked")
public <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c) {
switch (c.getSimpleName()) {
case "BreakIteratorProvider":
return (P) getBreakIteratorProvider();
case "CollatorProvider":
return (P) getCollatorProvider();
case "DateFormatProvider":
return (P) getDateFormatProvider();
case "DateFormatSymbolsProvider":
return (P) getDateFormatSymbolsProvider();
case "DecimalFormatSymbolsProvider":
return (P) getDecimalFormatSymbolsProvider();
case "NumberFormatProvider":
return (P) getNumberFormatProvider();
case "CurrencyNameProvider":
return (P) getCurrencyNameProvider();
case "LocaleNameProvider":
return (P) getLocaleNameProvider();
case "TimeZoneNameProvider":
return (P) getTimeZoneNameProvider();
case "CalendarDataProvider":
return (P) getCalendarDataProvider();
case "CalendarNameProvider":
return (P) getCalendarNameProvider();
case "CalendarProvider":
return (P) getCalendarProvider();
default:
throw new InternalError("should not come down here");
}
}
private volatile BreakIteratorProvider breakIteratorProvider = null;
private volatile CollatorProvider collatorProvider = null;
private volatile DateFormatProvider dateFormatProvider = null;
private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider = null;
private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider = null;
private volatile NumberFormatProvider numberFormatProvider = null;
private volatile CurrencyNameProvider currencyNameProvider = null;
private volatile LocaleNameProvider localeNameProvider = null;
private volatile TimeZoneNameProvider timeZoneNameProvider = null;
private volatile CalendarDataProvider calendarDataProvider = null;
private volatile CalendarNameProvider calendarNameProvider = null;
private volatile CalendarProvider calendarProvider = null;
/*
* Getter methods for java.text.spi.* providers
*/
@Override
public BreakIteratorProvider getBreakIteratorProvider() {
if (breakIteratorProvider == null) {
BreakIteratorProvider provider = new BreakIteratorProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (breakIteratorProvider == null) {
breakIteratorProvider = provider;
}
}
}
return breakIteratorProvider;
}
@Override
public CollatorProvider getCollatorProvider() {
if (collatorProvider == null) {
CollatorProvider provider = new CollatorProviderImpl(getAdapterType(),
getLanguageTagSet("CollationData"));
synchronized (this) {
if (collatorProvider == null) {
collatorProvider = provider;
}
}
}
return collatorProvider;
}
@Override
public DateFormatProvider getDateFormatProvider() {
if (dateFormatProvider == null) {
DateFormatProvider provider = new DateFormatProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (dateFormatProvider == null) {
dateFormatProvider = provider;
}
}
}
return dateFormatProvider;
}
@Override
public DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
if (dateFormatSymbolsProvider == null) {
DateFormatSymbolsProvider provider = new DateFormatSymbolsProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (dateFormatSymbolsProvider == null) {
dateFormatSymbolsProvider = provider;
}
}
}
return dateFormatSymbolsProvider;
}
@Override
public DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
if (decimalFormatSymbolsProvider == null) {
DecimalFormatSymbolsProvider provider = new DecimalFormatSymbolsProviderImpl(getAdapterType(), getLanguageTagSet("FormatData"));
synchronized (this) {
if (decimalFormatSymbolsProvider == null) {
decimalFormatSymbolsProvider = provider;
}
}
}
return decimalFormatSymbolsProvider;
}
@Override
public NumberFormatProvider getNumberFormatProvider() {
if (numberFormatProvider == null) {
NumberFormatProvider provider = new NumberFormatProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (numberFormatProvider == null) {
numberFormatProvider = provider;
}
}
}
return numberFormatProvider;
}
/**
* Getter methods for java.util.spi.* providers
*/
@Override
public CurrencyNameProvider getCurrencyNameProvider() {
if (currencyNameProvider == null) {
CurrencyNameProvider provider = new CurrencyNameProviderImpl(getAdapterType(),
getLanguageTagSet("CurrencyNames"));
synchronized (this) {
if (currencyNameProvider == null) {
currencyNameProvider = provider;
}
}
}
return currencyNameProvider;
}
@Override
public LocaleNameProvider getLocaleNameProvider() {
if (localeNameProvider == null) {
LocaleNameProvider provider = new LocaleNameProviderImpl(getAdapterType(),
getLanguageTagSet("LocaleNames"));
synchronized (this) {
if (localeNameProvider == null) {
localeNameProvider = provider;
}
}
}
return localeNameProvider;
}
@Override
public TimeZoneNameProvider getTimeZoneNameProvider() {
if (timeZoneNameProvider == null) {
TimeZoneNameProvider provider = new TimeZoneNameProviderImpl(getAdapterType(),
getLanguageTagSet("TimeZoneNames"));
synchronized (this) {
if (timeZoneNameProvider == null) {
timeZoneNameProvider = provider;
}
}
}
return timeZoneNameProvider;
}
@Override
public CalendarDataProvider getCalendarDataProvider() {
if (calendarDataProvider == null) {
CalendarDataProvider provider;
provider = new CalendarDataProviderImpl(getAdapterType(),
getLanguageTagSet("CalendarData"));
synchronized (this) {
if (calendarDataProvider == null) {
calendarDataProvider = provider;
}
}
}
return calendarDataProvider;
}
@Override
public CalendarNameProvider getCalendarNameProvider() {
if (calendarNameProvider == null) {
CalendarNameProvider provider;
provider = new CalendarNameProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (calendarNameProvider == null) {
calendarNameProvider = provider;
}
}
}
return calendarNameProvider;
}
/**
* Getter methods for sun.util.spi.* providers
*/
@Override
public CalendarProvider getCalendarProvider() {
if (calendarProvider == null) {
CalendarProvider provider = new CalendarProviderImpl(getAdapterType(),
getLanguageTagSet("CalendarData"));
synchronized (this) {
if (calendarProvider == null) {
calendarProvider = provider;
}
}
}
return calendarProvider;
}
@Override
public LocaleResources getLocaleResources(Locale locale) {
LocaleResources lr = localeResourcesMap.get(locale);
if (lr == null) {
lr = new LocaleResources(this, locale);
LocaleResources lrc = localeResourcesMap.putIfAbsent(locale, lr);
if (lrc != null) {
lr = lrc;
}
}
return lr;
}
// ResourceBundleBasedAdapter method implementation
@Override
public LocaleData getLocaleData() {
if (localeData == null) {
synchronized (this) {
if (localeData == null) {
localeData = new LocaleData(getAdapterType());
}
}
}
return localeData;
}
/**
* Returns a list of the installed locales. Currently, this simply returns
* the list of locales for which a sun.text.resources.FormatData bundle
* exists. This bundle family happens to be the one with the broadest
* locale coverage in the JRE.
*/
@Override
public Locale[] getAvailableLocales() {
return AvailableJRELocales.localeList.clone();
}
public Set<String> getLanguageTagSet(String category) {
Set<String> tagset = langtagSets.get(category);
if (tagset == null) {
tagset = createLanguageTagSet(category);
Set<String> ts = langtagSets.putIfAbsent(category, tagset);
if (ts != null) {
tagset = ts;
}
}
return tagset;
}
protected Set<String> createLanguageTagSet(String category) {
String supportedLocaleString = LocaleDataMetaInfo.getSupportedLocaleString(category);
if (supportedLocaleString == null) {
return Collections.emptySet();
}
Set<String> tagset = new HashSet<>();
StringTokenizer tokens = new StringTokenizer(supportedLocaleString);
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
if (token.equals("|")) {
if (isNonENLangSupported()) {
continue;
}
break;
}
tagset.add(token);
}
return tagset;
}
/**
* Lazy load available locales.
*/
private static class AvailableJRELocales {
private static final Locale[] localeList = createAvailableLocales();
private AvailableJRELocales() {
}
}
private static Locale[] createAvailableLocales() {
/*
* Gets the locale string list from LocaleDataMetaInfo class and then
* contructs the Locale array and a set of language tags based on the
* locale string returned above.
*/
String supportedLocaleString = LocaleDataMetaInfo.getSupportedLocaleString("AvailableLocales");
if (supportedLocaleString.length() == 0) {
throw new InternalError("No available locales for JRE");
}
/*
* Look for "|" and construct a new locale string list.
*/
int barIndex = supportedLocaleString.indexOf('|');
StringTokenizer localeStringTokenizer;
if (isNonENLangSupported()) {
localeStringTokenizer = new StringTokenizer(supportedLocaleString.substring(0, barIndex)
+ supportedLocaleString.substring(barIndex + 1));
} else {
localeStringTokenizer = new StringTokenizer(supportedLocaleString.substring(0, barIndex));
}
int length = localeStringTokenizer.countTokens();
Locale[] locales = new Locale[length + 1];
locales[0] = Locale.ROOT;
for (int i = 1; i <= length; i++) {
String currentToken = localeStringTokenizer.nextToken();
switch (currentToken) {
case "ja-JP-JP":
locales[i] = JRELocaleConstants.JA_JP_JP;
break;
case "no-NO-NY":
locales[i] = JRELocaleConstants.NO_NO_NY;
break;
case "th-TH-TH":
locales[i] = JRELocaleConstants.TH_TH_TH;
break;
default:
locales[i] = Locale.forLanguageTag(currentToken);
}
}
return locales;
}
private static volatile Boolean isNonENSupported = null;
/*
* Returns true if the non EN resources jar file exists in jre
* extension directory. @returns true if the jar file is there. Otherwise,
* returns false.
*/
private static boolean isNonENLangSupported() {
if (isNonENSupported == null) {
synchronized (JRELocaleProviderAdapter.class) {
if (isNonENSupported == null) {
final String sep = File.separator;
String localeDataJar =
java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("java.home"))
+ sep + "lib" + sep + "ext" + sep + LOCALE_DATA_JAR_NAME;
/*
* Peek at the installed extension directory to see if
* localedata.jar is installed or not.
*/
final File f = new File(localeDataJar);
isNonENSupported =
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return f.exists();
}
});
}
}
}
return isNonENSupported;
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2005, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
// -- This file was mechanically generated: Do not edit! -- //
/*
* This class contains a map which records the locale list string for
* each resource in sun.util.resources & sun.text.resources.
* It is used to avoid loading non-existent localized resources so that
* jar files won't be opened unnecessary to look up them.
*
* @since 1.6
*/
package sun.util.locale.provider;
import java.util.HashMap;
public class LocaleDataMetaInfo {
private static final HashMap<String, String> resourceNameToLocales =
new HashMap<String, String>(7);
static {
/* During JDK build time, #XXX_YYY# will be replaced by a string contain all the locales
supported by the resource.
Don't remove the space character between " and #. That is put there purposely so that
look up locale string such as "en" could be based on if it contains " en ".
*/
resourceNameToLocales.put("FormatData",
" en en-AU en-CA en-GB en-IE en-IN en-MT en-NZ en-PH en-SG en-US en-ZA | ar ar-JO ar-LB ar-SY be be-BY bg bg-BG ca ca-ES cs cs-CZ da da-DK de de-AT de-CH de-DE de-LU el el-CY el-GR es es-AR es-BO es-CL es-CO es-CR es-DO es-EC es-ES es-GT es-HN es-MX es-NI es-PA es-PE es-PR es-PY es-SV es-US es-UY es-VE et et-EE fi fi-FI fr fr-BE fr-CA fr-CH fr-FR ga ga-IE hi-IN hr hr-HR hu hu-HU in in-ID is is-IS it it-CH it-IT iw iw-IL ja ja-JP ko ko-KR lt lt-LT lv lv-LV mk mk-MK ms ms-MY mt mt-MT nl nl-BE nl-NL no no-NO no-NO-NY pl pl-PL pt pt-BR pt-PT ro ro-RO ru ru-RU sk sk-SK sl sl-SI sq sq-AL sr sr-BA sr-CS sr-Latn sr-Latn-ME sr-ME sr-RS sv sv-SE th th-TH tr tr-TR uk uk-UA vi vi-VN zh zh-CN zh-HK zh-SG zh-TW ");
resourceNameToLocales.put("CollationData",
" | ar be bg ca cs da el es et fi fr hi hr hu is iw ja ko lt lv mk no pl ro ru sk sl sq sr sr-Latn sv th tr uk vi zh zh-HK zh-TW ");
resourceNameToLocales.put("BreakIteratorInfo",
" | th ");
resourceNameToLocales.put("BreakIteratorRules",
" | th ");
resourceNameToLocales.put("TimeZoneNames",
" en en-CA en-GB en-IE | de es fr hi it ja ko pt-BR sv zh-CN zh-HK zh-TW ");
resourceNameToLocales.put("LocaleNames",
" en en-MT en-PH en-SG | ar be bg ca cs da de el el-CY es es-US et fi fr ga hi hr hu in is it iw ja ko lt lv mk ms mt nl no no-NO-NY pl pt pt-PT ro ru sk sl sq sr sr-Latn sv th tr uk vi zh zh-HK zh-SG zh-TW ");
resourceNameToLocales.put("CurrencyNames",
" en-AU en-CA en-GB en-IE en-IN en-MT en-NZ en-PH en-SG en-US en-ZA | ar-AE ar-BH ar-DZ ar-EG ar-IQ ar-JO ar-KW ar-LB ar-LY ar-MA ar-OM ar-QA ar-SA ar-SD ar-SY ar-TN ar-YE be-BY bg-BG ca-ES cs-CZ da-DK de de-AT de-CH de-DE de-GR de-LU el-CY el-GR es es-AR es-BO es-CL es-CO es-CR es-CU es-DO es-EC es-ES es-GT es-HN es-MX es-NI es-PA es-PE es-PR es-PY es-SV es-US es-UY es-VE et-EE fi-FI fr fr-BE fr-CA fr-CH fr-FR fr-LU ga-IE hi-IN hr-HR hu-HU in-ID is-IS it it-CH it-IT iw-IL ja ja-JP ko ko-KR lt-LT lv-LV mk-MK ms-MY mt-MT nl-BE nl-NL no-NO pl-PL pt pt-BR pt-PT ro-RO ru-RU sk-SK sl-SI sq-AL sr-BA sr-CS sr-Latn-BA sr-Latn-ME sr-Latn-RS sr-ME sr-RS sv sv-SE th-TH tr-TR uk-UA vi-VN zh-CN zh-HK zh-SG zh-TW ");
resourceNameToLocales.put("CalendarData",
" en en-GB en-IE en-MT | ar be bg ca cs da de el el-CY es es-ES es-US et fi fr fr-CA hi hr hu in-ID is it iw ja ko lt lv mk ms-MY mt mt-MT nl no pl pt pt-BR pt-PT ro ru sk sl sq sr sr-Latn-BA sr-Latn-ME sr-Latn-RS sv th tr uk vi zh ");
resourceNameToLocales.put("AvailableLocales",
" en en-AU en-CA en-GB en-IE en-IN en-MT en-NZ en-PH en-SG en-US en-ZA | ar ar-AE ar-BH ar-DZ ar-EG ar-IQ ar-JO ar-KW ar-LB ar-LY ar-MA ar-OM ar-QA ar-SA ar-SD ar-SY ar-TN ar-YE be be-BY bg bg-BG ca ca-ES cs cs-CZ da da-DK de de-AT de-CH de-DE de-GR de-LU el el-CY el-GR es es-AR es-BO es-CL es-CO es-CR es-CU es-DO es-EC es-ES es-GT es-HN es-MX es-NI es-PA es-PE es-PR es-PY es-SV es-US es-UY es-VE et et-EE fi fi-FI fr fr-BE fr-CA fr-CH fr-FR fr-LU ga ga-IE hi hi-IN hr hr-HR hu hu-HU in in-ID is is-IS it it-CH it-IT iw iw-IL ja ja-JP ja-JP-JP ko ko-KR lt lt-LT lv lv-LV mk mk-MK ms ms-MY mt mt-MT nl nl-BE nl-NL no no-NO no-NO-NY pl pl-PL pt pt-BR pt-PT ro ro-RO ru ru-RU sk sk-SK sl sl-SI sq sq-AL sr sr-BA sr-CS sr-Latn sr-Latn-BA sr-Latn-ME sr-Latn-RS sr-ME sr-RS sv sv-SE th th-TH th-TH-TH tr tr-TR uk uk-UA vi vi-VN zh zh-CN zh-HK zh-SG zh-TW ");
}
/*
* @param resourceName the resource name
* @return the supported locale string for the passed in resource.
*/
public static String getSupportedLocaleString(String resourceName) {
return resourceNameToLocales.get(resourceName);
}
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.Locale;
import java.util.Set;
import java.util.spi.LocaleNameProvider;
/**
* Concrete implementation of the
* {@link java.util.spi.LocaleNameProvider LocaleNameProvider} class
* for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class LocaleNameProviderImpl extends LocaleNameProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public LocaleNameProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a localized name for the given ISO 639 language code and the
* given locale that is appropriate for display to the user.
* For example, if <code>languageCode</code> is "fr" and <code>locale</code>
* is en_US, getDisplayLanguage() will return "French"; if <code>languageCode</code>
* is "en" and <code>locale</code> is fr_FR, getDisplayLanguage() will return "anglais".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Croatian),
* this method returns null.
* @param languageCode the ISO 639 language code string in the form of two
* lower-case letters between 'a' (U+0061) and 'z' (U+007A)
* @param locale the desired locale
* @return the name of the given language code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>languageCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>languageCode</code> is not in the form of
* two lower-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayLanguage(java.util.Locale)
*/
@Override
public String getDisplayLanguage(String lang, Locale locale) {
return getDisplayString(lang, locale);
}
/**
* Returns a localized name for the given <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
* IETF BCP47</a> script code and the given locale that is appropriate for
* display to the user.
* For example, if <code>scriptCode</code> is "Latn" and <code>locale</code>
* is en_US, getDisplayScript() will return "Latin"; if <code>scriptCode</code>
* is "Cyrl" and <code>locale</code> is fr_FR, getDisplayScript() will return "cyrillique".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Cyrillic),
* this method returns null. The default implementation returns null.
* @param scriptCode the four letter script code string in the form of title-case
* letters (the first letter is upper-case character between 'A' (U+0041) and
* 'Z' (U+005A) followed by three lower-case character between 'a' (U+0061)
* and 'z' (U+007A)).
* @param locale the desired locale
* @return the name of the given script code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>scriptCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>scriptCode</code> is not in the form of
* four title case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayScript(java.util.Locale)
*/
@Override
public String getDisplayScript(String scriptCode, Locale locale) {
return getDisplayString(scriptCode, locale);
}
/**
* Returns a localized name for the given ISO 3166 country code and the
* given locale that is appropriate for display to the user.
* For example, if <code>countryCode</code> is "FR" and <code>locale</code>
* is en_US, getDisplayCountry() will return "France"; if <code>countryCode</code>
* is "US" and <code>locale</code> is fr_FR, getDisplayCountry() will return "Etats-Unis".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Croatia),
* this method returns null.
* @param countryCode the ISO 3166 country code string in the form of two
* upper-case letters between 'A' (U+0041) and 'Z' (U+005A)
* @param locale the desired locale
* @return the name of the given country code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>countryCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>countryCode</code> is not in the form of
* two upper-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayCountry(java.util.Locale)
*/
@Override
public String getDisplayCountry(String ctry, Locale locale) {
return getDisplayString(ctry, locale);
}
/**
* Returns a localized name for the given variant code and the given locale that
* is appropriate for display to the user.
* If the name returned cannot be localized according to <code>locale</code>,
* this method returns null.
* @param variant the variant string
* @param locale the desired locale
* @return the name of the given variant string for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>variant</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayVariant(java.util.Locale)
*/
@Override
public String getDisplayVariant(String vrnt, Locale locale) {
return getDisplayString("%%"+vrnt, locale);
}
private String getDisplayString(String key, Locale locale) {
if (key == null || locale == null) {
throw new NullPointerException();
}
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getLocaleName(key);
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,456 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.security.AccessController;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
import sun.util.cldr.CLDRLocaleProviderAdapter;
import sun.util.spi.CalendarProvider;
/**
* The LocaleProviderAdapter abstract class.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public abstract class LocaleProviderAdapter {
/**
* Adapter type.
*/
public static enum Type {
JRE("sun.util.resources", "sun.text.resources"),
CLDR("sun.util.resources.cldr", "sun.text.resources.cldr"),
SPI,
HOST,
FALLBACK("sun.util.resources", "sun.text.resources");
private final String UTIL_RESOURCES_PACKAGE;
private final String TEXT_RESOURCES_PACKAGE;
private Type() {
this(null, null);
}
private Type(String util, String text) {
UTIL_RESOURCES_PACKAGE = util;
TEXT_RESOURCES_PACKAGE = text;
}
public String getUtilResourcesPackage() {
return UTIL_RESOURCES_PACKAGE;
}
public String getTextResourcesPackage() {
return TEXT_RESOURCES_PACKAGE;
}
}
/**
* LocaleProviderAdapter preference list. The default list is intended
* to behave the same manner in JDK7.
*/
private static final List<Type> adapterPreference;
/**
* JRE Locale Data Adapter instance
*/
private static LocaleProviderAdapter jreLocaleProviderAdapter = new JRELocaleProviderAdapter();
/**
* SPI Locale Data Adapter instance
*/
private static LocaleProviderAdapter spiLocaleProviderAdapter = new SPILocaleProviderAdapter();
/**
* CLDR Locale Data Adapter instance, if any.
*/
private static LocaleProviderAdapter cldrLocaleProviderAdapter = null;
/**
* HOST Locale Data Adapter instance, if any.
*/
private static LocaleProviderAdapter hostLocaleProviderAdapter = null;
/**
* FALLBACK Locale Data Adapter instance. It's basically the same with JRE, but only kicks
* in for the root locale.
*/
private static LocaleProviderAdapter fallbackLocaleProviderAdapter = null;
/**
* Default fallback adapter type, which should return something meaningful in any case.
* This is either JRE or FALLBACK.
*/
static LocaleProviderAdapter.Type defaultLocaleProviderAdapter = null;
/**
* Adapter lookup cache.
*/
private static ConcurrentMap<Class<? extends LocaleServiceProvider>, ConcurrentMap<Locale, LocaleProviderAdapter>>
adapterCache = new ConcurrentHashMap<>();
static {
String order = AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("java.locale.providers"));
List<Type> typeList = new ArrayList<>();
// Check user specified adapter preference
if (order != null && order.length() != 0) {
String[] types = order.split(",");
for (String type : types) {
try {
Type aType = Type.valueOf(type.trim().toUpperCase(Locale.ROOT));
// load adapter if necessary
switch (aType) {
case CLDR:
if (cldrLocaleProviderAdapter == null) {
cldrLocaleProviderAdapter = new CLDRLocaleProviderAdapter();
}
break;
case HOST:
if (hostLocaleProviderAdapter == null) {
hostLocaleProviderAdapter = new HostLocaleProviderAdapter();
}
break;
}
if (!typeList.contains(aType)) {
typeList.add(aType);
}
} catch (IllegalArgumentException | UnsupportedOperationException e) {
// could be caused by the user specifying wrong
// provider name or format in the system property
LocaleServiceProviderPool.config(LocaleProviderAdapter.class, e.toString());
}
}
}
if (!typeList.isEmpty()) {
if (!typeList.contains(Type.JRE)) {
// Append FALLBACK as the last resort.
fallbackLocaleProviderAdapter = new FallbackLocaleProviderAdapter();
typeList.add(Type.FALLBACK);
defaultLocaleProviderAdapter = Type.FALLBACK;
} else {
defaultLocaleProviderAdapter = Type.JRE;
}
} else {
// Default preference list
typeList.add(Type.JRE);
typeList.add(Type.SPI);
defaultLocaleProviderAdapter = Type.JRE;
}
adapterPreference = Collections.unmodifiableList(typeList);
}
/**
* Returns the singleton instance for each adapter type
*/
public static LocaleProviderAdapter forType(Type type) {
switch (type) {
case JRE:
return jreLocaleProviderAdapter;
case CLDR:
return cldrLocaleProviderAdapter;
case SPI:
return spiLocaleProviderAdapter;
case HOST:
return hostLocaleProviderAdapter;
case FALLBACK:
return fallbackLocaleProviderAdapter;
default:
throw new InternalError("unknown locale data adapter type");
}
}
public static LocaleProviderAdapter forJRE() {
return jreLocaleProviderAdapter;
}
public static LocaleProviderAdapter getResourceBundleBased() {
for (Type type : getAdapterPreference()) {
if (type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK) {
return forType(type);
}
}
// Shouldn't happen.
throw new InternalError();
}
/**
* Returns the preference order of LocaleProviderAdapter.Type
*/
public static List<Type> getAdapterPreference() {
return adapterPreference;
}
/**
* Returns a LocaleProviderAdapter for the given locale service provider that
* best matches the given locale. This method returns the LocaleProviderAdapter
* for JRE if none is found for the given locale.
*
* @param providerClass the class for the locale service provider
* @param locale the desired locale.
* @return a LocaleProviderAdapter
*/
public static LocaleProviderAdapter getAdapter(Class<? extends LocaleServiceProvider> providerClass,
Locale locale) {
LocaleProviderAdapter adapter;
// cache lookup
ConcurrentMap<Locale, LocaleProviderAdapter> adapterMap = adapterCache.get(providerClass);
if (adapterMap != null) {
if ((adapter = adapterMap.get(locale)) != null) {
return adapter;
}
} else {
adapterMap = new ConcurrentHashMap<>();
adapterCache.putIfAbsent(providerClass, adapterMap);
}
// Fast look-up for the given locale
adapter = findAdapter(providerClass, locale);
if (adapter != null) {
adapterMap.putIfAbsent(locale, adapter);
return adapter;
}
// Try finding an adapter in the normal candidate locales path of the given locale.
List<Locale> lookupLocales = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT)
.getCandidateLocales("", locale);
for (Locale loc : lookupLocales) {
if (loc.equals(locale)) {
// We've already done with this loc.
continue;
}
adapter = findAdapter(providerClass, loc);
if (adapter != null) {
adapterMap.putIfAbsent(locale, adapter);
return adapter;
}
}
// returns the adapter for FALLBACK as the last resort
adapterMap.putIfAbsent(locale, fallbackLocaleProviderAdapter);
return fallbackLocaleProviderAdapter;
}
private static LocaleProviderAdapter findAdapter(Class<? extends LocaleServiceProvider> providerClass,
Locale locale) {
for (Type type : getAdapterPreference()) {
LocaleProviderAdapter adapter = forType(type);
LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass);
if (provider != null) {
if (provider.isSupportedLocale(locale)) {
return adapter;
}
}
}
return null;
}
/**
* A utility method for implementing the default LocaleServiceProvider.isSupportedLocale
* for the JRE, CLDR, and FALLBACK adapters.
*/
public static boolean isSupportedLocale(Locale locale, LocaleProviderAdapter.Type type, Set<String> langtags) {
assert type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK;
if (Locale.ROOT.equals(locale)) {
return true;
}
if (type == Type.FALLBACK) {
// no other locales except ROOT are supported for FALLBACK
return false;
}
locale = locale.stripExtensions();
if (langtags.contains(locale.toLanguageTag())) {
return true;
}
if (type == Type.JRE) {
String oldname = locale.toString().replace('_', '-');
return langtags.contains(oldname) ||
"ja-JP-JP".equals(oldname) ||
"th-TH-TH".equals(oldname) ||
"no-NO-NY".equals(oldname);
}
return false;
}
public static Locale[] toLocaleArray(Set<String> tags) {
Locale[] locs = new Locale[tags.size() + 1];
int index = 0;
locs[index++] = Locale.ROOT;
for (String tag : tags) {
switch (tag) {
case "ja-JP-JP":
locs[index++] = JRELocaleConstants.JA_JP_JP;
break;
case "th-TH-TH":
locs[index++] = JRELocaleConstants.TH_TH_TH;
break;
default:
locs[index++] = Locale.forLanguageTag(tag);
break;
}
}
return locs;
}
/**
* Returns the type of this LocaleProviderAdapter
*/
public abstract LocaleProviderAdapter.Type getAdapterType();
/**
* Getter method for Locale Service Providers.
*/
public abstract <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c);
/**
* Returns a BreakIteratorProvider for this LocaleProviderAdapter, or null if no
* BreakIteratorProvider is available.
*
* @return a BreakIteratorProvider
*/
public abstract BreakIteratorProvider getBreakIteratorProvider();
/**
* Returns a ollatorProvider for this LocaleProviderAdapter, or null if no
* ollatorProvider is available.
*
* @return a ollatorProvider
*/
public abstract CollatorProvider getCollatorProvider();
/**
* Returns a DateFormatProvider for this LocaleProviderAdapter, or null if no
* DateFormatProvider is available.
*
* @return a DateFormatProvider
*/
public abstract DateFormatProvider getDateFormatProvider();
/**
* Returns a DateFormatSymbolsProvider for this LocaleProviderAdapter, or null if no
* DateFormatSymbolsProvider is available.
*
* @return a DateFormatSymbolsProvider
*/
public abstract DateFormatSymbolsProvider getDateFormatSymbolsProvider();
/**
* Returns a DecimalFormatSymbolsProvider for this LocaleProviderAdapter, or null if no
* DecimalFormatSymbolsProvider is available.
*
* @return a DecimalFormatSymbolsProvider
*/
public abstract DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider();
/**
* Returns a NumberFormatProvider for this LocaleProviderAdapter, or null if no
* NumberFormatProvider is available.
*
* @return a NumberFormatProvider
*/
public abstract NumberFormatProvider getNumberFormatProvider();
/*
* Getter methods for java.util.spi.* providers
*/
/**
* Returns a CurrencyNameProvider for this LocaleProviderAdapter, or null if no
* CurrencyNameProvider is available.
*
* @return a CurrencyNameProvider
*/
public abstract CurrencyNameProvider getCurrencyNameProvider();
/**
* Returns a LocaleNameProvider for this LocaleProviderAdapter, or null if no
* LocaleNameProvider is available.
*
* @return a LocaleNameProvider
*/
public abstract LocaleNameProvider getLocaleNameProvider();
/**
* Returns a TimeZoneNameProvider for this LocaleProviderAdapter, or null if no
* TimeZoneNameProvider is available.
*
* @return a TimeZoneNameProvider
*/
public abstract TimeZoneNameProvider getTimeZoneNameProvider();
/**
* Returns a CalendarDataProvider for this LocaleProviderAdapter, or null if no
* CalendarDataProvider is available.
*
* @return a CalendarDataProvider
*/
public abstract CalendarDataProvider getCalendarDataProvider();
/**
* Returns a CalendarNameProvider for this LocaleProviderAdapter, or null if no
* CalendarNameProvider is available.
*
* @return a CalendarNameProvider
*/
public abstract CalendarNameProvider getCalendarNameProvider();
/**
* Returns a CalendarProvider for this LocaleProviderAdapter, or null if no
* CalendarProvider is available.
*
* @return a CalendarProvider
*/
public abstract CalendarProvider getCalendarProvider();
public abstract LocaleResources getLocaleResources(Locale locale);
public abstract Locale[] getAvailableLocales();
}

View File

@@ -0,0 +1,510 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package sun.util.locale.provider;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.text.MessageFormat;
import java.util.Calendar;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import sun.util.calendar.ZoneInfo;
import sun.util.resources.LocaleData;
import sun.util.resources.OpenListResourceBundle;
import sun.util.resources.ParallelListResourceBundle;
import sun.util.resources.TimeZoneNamesBundle;
/**
* Central accessor to locale-dependent resources for JRE/CLDR provider adapters.
*
* @author Masayoshi Okutsu
* @author Naoto Sato
*/
public class LocaleResources {
private final Locale locale;
private final LocaleData localeData;
private final LocaleProviderAdapter.Type type;
// Resource cache
private ConcurrentMap<String, ResourceReference> cache = new ConcurrentHashMap<>();
private ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// cache key prefixes
private static final String BREAK_ITERATOR_INFO = "BII.";
private static final String CALENDAR_DATA = "CALD.";
private static final String COLLATION_DATA_CACHEKEY = "COLD";
private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD";
private static final String CURRENCY_NAMES = "CN.";
private static final String LOCALE_NAMES = "LN.";
private static final String TIME_ZONE_NAMES = "TZN.";
private static final String ZONE_IDS_CACHEKEY = "ZID";
private static final String CALENDAR_NAMES = "CALN.";
private static final String NUMBER_PATTERNS_CACHEKEY = "NP";
private static final String DATE_TIME_PATTERN = "DTP.";
// null singleton cache value
private static final Object NULLOBJECT = new Object();
LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) {
this.locale = locale;
this.localeData = adapter.getLocaleData();
type = ((LocaleProviderAdapter)adapter).getAdapterType();
}
private void removeEmptyReferences() {
Object ref;
while ((ref = referenceQueue.poll()) != null) {
cache.remove(((ResourceReference)ref).getCacheKey());
}
}
Object getBreakIteratorInfo(String key) {
Object biInfo;
String cacheKey = BREAK_ITERATOR_INFO + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((biInfo = data.get()) == null)) {
biInfo = localeData.getBreakIteratorInfo(locale).getObject(key);
cache.put(cacheKey, new ResourceReference(cacheKey, biInfo, referenceQueue));
}
return biInfo;
}
int getCalendarData(String key) {
Integer caldata;
String cacheKey = CALENDAR_DATA + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((caldata = (Integer) data.get()) == null)) {
ResourceBundle rb = localeData.getCalendarData(locale);
if (rb.containsKey(key)) {
caldata = Integer.parseInt(rb.getString(key));
} else {
caldata = 0;
}
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) caldata, referenceQueue));
}
return caldata;
}
public String getCollationData() {
String key = "Rule";
String coldata = "";
removeEmptyReferences();
ResourceReference data = cache.get(COLLATION_DATA_CACHEKEY);
if (data == null || ((coldata = (String) data.get()) == null)) {
ResourceBundle rb = localeData.getCollationData(locale);
if (rb.containsKey(key)) {
coldata = rb.getString(key);
}
cache.put(COLLATION_DATA_CACHEKEY,
new ResourceReference(COLLATION_DATA_CACHEKEY, (Object) coldata, referenceQueue));
}
return coldata;
}
public Object[] getDecimalFormatSymbolsData() {
Object[] dfsdata;
removeEmptyReferences();
ResourceReference data = cache.get(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY);
if (data == null || ((dfsdata = (Object[]) data.get()) == null)) {
// Note that only dfsdata[0] is prepared here in this method. Other
// elements are provided by the caller, yet they are cached here.
ResourceBundle rb = localeData.getNumberFormatData(locale);
dfsdata = new Object[3];
// NumberElements look up. First, try the Unicode extension
String numElemKey;
String numberType = locale.getUnicodeLocaleType("nu");
if (numberType != null) {
numElemKey = numberType + ".NumberElements";
if (rb.containsKey(numElemKey)) {
dfsdata[0] = rb.getStringArray(numElemKey);
}
}
// Next, try DefaultNumberingSystem value
if (dfsdata[0] == null && rb.containsKey("DefaultNumberingSystem")) {
numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements";
if (rb.containsKey(numElemKey)) {
dfsdata[0] = rb.getStringArray(numElemKey);
}
}
// Last resort. No need to check the availability.
// Just let it throw MissingResourceException when needed.
if (dfsdata[0] == null) {
dfsdata[0] = rb.getStringArray("NumberElements");
}
cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,
new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue));
}
return dfsdata;
}
public String getCurrencyName(String key) {
Object currencyName = null;
String cacheKey = CURRENCY_NAMES + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data != null && ((currencyName = data.get()) != null)) {
if (currencyName.equals(NULLOBJECT)) {
currencyName = null;
}
return (String) currencyName;
}
OpenListResourceBundle olrb = localeData.getCurrencyNames(locale);
if (olrb.containsKey(key)) {
currencyName = olrb.getObject(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, currencyName, referenceQueue));
}
return (String) currencyName;
}
public String getLocaleName(String key) {
Object localeName = null;
String cacheKey = LOCALE_NAMES + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data != null && ((localeName = data.get()) != null)) {
if (localeName.equals(NULLOBJECT)) {
localeName = null;
}
return (String) localeName;
}
OpenListResourceBundle olrb = localeData.getLocaleNames(locale);
if (olrb.containsKey(key)) {
localeName = olrb.getObject(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, localeName, referenceQueue));
}
return (String) localeName;
}
String[] getTimeZoneNames(String key) {
String[] names = null;
String cacheKey = TIME_ZONE_NAMES + '.' + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (Objects.isNull(data) || Objects.isNull((names = (String[]) data.get()))) {
TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
if (tznb.containsKey(key)) {
names = tznb.getStringArray(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) names, referenceQueue));
}
}
return names;
}
@SuppressWarnings("unchecked")
Set<String> getZoneIDs() {
Set<String> zoneIDs = null;
removeEmptyReferences();
ResourceReference data = cache.get(ZONE_IDS_CACHEKEY);
if (data == null || ((zoneIDs = (Set<String>) data.get()) == null)) {
TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
zoneIDs = rb.keySet();
cache.put(ZONE_IDS_CACHEKEY,
new ResourceReference(ZONE_IDS_CACHEKEY, (Object) zoneIDs, referenceQueue));
}
return zoneIDs;
}
// zoneStrings are cached separately in TimeZoneNameUtility.
String[][] getZoneStrings() {
TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
Set<String> keyset = getZoneIDs();
// Use a LinkedHashSet to preseve the order
Set<String[]> value = new LinkedHashSet<>();
for (String key : keyset) {
value.add(rb.getStringArray(key));
}
// Add aliases data for CLDR
if (type == LocaleProviderAdapter.Type.CLDR) {
// Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
Map<String, String> aliases = ZoneInfo.getAliasTable();
for (String alias : aliases.keySet()) {
if (!keyset.contains(alias)) {
String tzid = aliases.get(alias);
if (keyset.contains(tzid)) {
String[] val = rb.getStringArray(tzid);
val[0] = alias;
value.add(val);
}
}
}
}
return value.toArray(new String[0][]);
}
String[] getCalendarNames(String key) {
String[] names = null;
String cacheKey = CALENDAR_NAMES + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((names = (String[]) data.get()) == null)) {
ResourceBundle rb = localeData.getDateFormatData(locale);
if (rb.containsKey(key)) {
names = rb.getStringArray(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) names, referenceQueue));
}
}
return names;
}
String[] getJavaTimeNames(String key) {
String[] names = null;
String cacheKey = CALENDAR_NAMES + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((names = (String[]) data.get()) == null)) {
ResourceBundle rb = getJavaTimeFormatData();
if (rb.containsKey(key)) {
names = rb.getStringArray(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) names, referenceQueue));
}
}
return names;
}
public String getDateTimePattern(int timeStyle, int dateStyle, Calendar cal) {
if (cal == null) {
cal = Calendar.getInstance(locale);
}
return getDateTimePattern(null, timeStyle, dateStyle, cal.getCalendarType());
}
/**
* Returns a date-time format pattern
* @param timeStyle style of time; one of FULL, LONG, MEDIUM, SHORT in DateFormat,
* or -1 if not required
* @param dateStyle style of time; one of FULL, LONG, MEDIUM, SHORT in DateFormat,
* or -1 if not required
* @param calType the calendar type for the pattern
* @return the pattern string
*/
public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType) {
calType = CalendarDataUtility.normalizeCalendarType(calType);
String pattern;
pattern = getDateTimePattern("java.time.", timeStyle, dateStyle, calType);
if (pattern == null) {
pattern = getDateTimePattern(null, timeStyle, dateStyle, calType);
}
return pattern;
}
private String getDateTimePattern(String prefix, int timeStyle, int dateStyle, String calType) {
String pattern;
String timePattern = null;
String datePattern = null;
if (timeStyle >= 0) {
if (prefix != null) {
timePattern = getDateTimePattern(prefix, "TimePatterns", timeStyle, calType);
}
if (timePattern == null) {
timePattern = getDateTimePattern(null, "TimePatterns", timeStyle, calType);
}
}
if (dateStyle >= 0) {
if (prefix != null) {
datePattern = getDateTimePattern(prefix, "DatePatterns", dateStyle, calType);
}
if (datePattern == null) {
datePattern = getDateTimePattern(null, "DatePatterns", dateStyle, calType);
}
}
if (timeStyle >= 0) {
if (dateStyle >= 0) {
String dateTimePattern = null;
if (prefix != null) {
dateTimePattern = getDateTimePattern(prefix, "DateTimePatterns", 0, calType);
}
if (dateTimePattern == null) {
dateTimePattern = getDateTimePattern(null, "DateTimePatterns", 0, calType);
}
switch (dateTimePattern) {
case "{1} {0}":
pattern = datePattern + " " + timePattern;
break;
case "{0} {1}":
pattern = timePattern + " " + datePattern;
break;
default:
pattern = MessageFormat.format(dateTimePattern, timePattern, datePattern);
break;
}
} else {
pattern = timePattern;
}
} else if (dateStyle >= 0) {
pattern = datePattern;
} else {
throw new IllegalArgumentException("No date or time style specified");
}
return pattern;
}
public String[] getNumberPatterns() {
String[] numberPatterns = null;
removeEmptyReferences();
ResourceReference data = cache.get(NUMBER_PATTERNS_CACHEKEY);
if (data == null || ((numberPatterns = (String[]) data.get()) == null)) {
ResourceBundle resource = localeData.getNumberFormatData(locale);
numberPatterns = resource.getStringArray("NumberPatterns");
cache.put(NUMBER_PATTERNS_CACHEKEY,
new ResourceReference(NUMBER_PATTERNS_CACHEKEY, (Object) numberPatterns, referenceQueue));
}
return numberPatterns;
}
/**
* Returns the FormatData resource bundle of this LocaleResources.
* The FormatData should be used only for accessing extra
* resources required by JSR 310.
*/
public ResourceBundle getJavaTimeFormatData() {
ResourceBundle rb = localeData.getDateFormatData(locale);
if (rb instanceof ParallelListResourceBundle) {
localeData.setSupplementary((ParallelListResourceBundle) rb);
}
return rb;
}
private String getDateTimePattern(String prefix, String key, int styleIndex, String calendarType) {
StringBuilder sb = new StringBuilder();
if (prefix != null) {
sb.append(prefix);
}
if (!"gregory".equals(calendarType)) {
sb.append(calendarType).append('.');
}
sb.append(key);
String resourceKey = sb.toString();
String cacheKey = sb.insert(0, DATE_TIME_PATTERN).toString();
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
Object value = NULLOBJECT;
if (data == null || ((value = data.get()) == null)) {
ResourceBundle r = (prefix != null) ? getJavaTimeFormatData() : localeData.getDateFormatData(locale);
if (r.containsKey(resourceKey)) {
value = r.getStringArray(resourceKey);
} else {
assert !resourceKey.equals(key);
if (r.containsKey(key)) {
value = r.getStringArray(key);
}
}
cache.put(cacheKey,
new ResourceReference(cacheKey, value, referenceQueue));
}
if (value == NULLOBJECT) {
assert prefix != null;
return null;
}
return ((String[])value)[styleIndex];
}
private static class ResourceReference extends SoftReference<Object> {
private final String cacheKey;
ResourceReference(String cacheKey, Object o, ReferenceQueue<Object> q) {
super(o, q);
this.cacheKey = cacheKey;
}
String getCacheKey() {
return cacheKey;
}
}
}

View File

@@ -0,0 +1,419 @@
/*
* Copyright (c) 2005, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.IllformedLocaleException;
import java.util.List;
import java.util.Locale;
import java.util.Locale.Builder;
import java.util.ResourceBundle.Control;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.LocaleServiceProvider;
import sun.util.logging.PlatformLogger;
/**
* An instance of this class holds a set of the third party implementations of a particular
* locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public final class LocaleServiceProviderPool {
/**
* A Map that holds singleton instances of this class. Each instance holds a
* set of provider implementations of a particular locale sensitive service.
*/
private static ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
new ConcurrentHashMap<>();
/**
* A Map containing locale service providers that implement the
* specified provider SPI, keyed by a LocaleProviderAdapter.Type
*/
private ConcurrentMap<LocaleProviderAdapter.Type, LocaleServiceProvider> providers =
new ConcurrentHashMap<>();
/**
* A Map that retains Locale->provider mapping
*/
private ConcurrentMap<Locale, List<LocaleProviderAdapter.Type>> providersCache =
new ConcurrentHashMap<>();
/**
* Available locales for this locale sensitive service. This also contains
* JRE's available locales
*/
private Set<Locale> availableLocales = null;
/**
* Provider class
*/
private Class<? extends LocaleServiceProvider> providerClass;
/**
* Array of all Locale Sensitive SPI classes.
*
* We know "spiClasses" contains classes that extends LocaleServiceProvider,
* but generic array creation is not allowed, thus the "unchecked" warning
* is suppressed here.
*/
@SuppressWarnings("unchecked")
static final Class<LocaleServiceProvider>[] spiClasses =
(Class<LocaleServiceProvider>[]) new Class<?>[] {
java.text.spi.BreakIteratorProvider.class,
java.text.spi.CollatorProvider.class,
java.text.spi.DateFormatProvider.class,
java.text.spi.DateFormatSymbolsProvider.class,
java.text.spi.DecimalFormatSymbolsProvider.class,
java.text.spi.NumberFormatProvider.class,
java.util.spi.CurrencyNameProvider.class,
java.util.spi.LocaleNameProvider.class,
java.util.spi.TimeZoneNameProvider.class,
java.util.spi.CalendarDataProvider.class
};
/**
* A factory method that returns a singleton instance
*/
public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {
LocaleServiceProviderPool pool = poolOfPools.get(providerClass);
if (pool == null) {
LocaleServiceProviderPool newPool =
new LocaleServiceProviderPool(providerClass);
pool = poolOfPools.putIfAbsent(providerClass, newPool);
if (pool == null) {
pool = newPool;
}
}
return pool;
}
/**
* The sole constructor.
*
* @param c class of the locale sensitive service
*/
private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
providerClass = c;
for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {
LocaleProviderAdapter lda = LocaleProviderAdapter.forType(type);
if (lda != null) {
LocaleServiceProvider provider = lda.getLocaleServiceProvider(c);
if (provider != null) {
providers.putIfAbsent(type, provider);
}
}
}
}
static void config(Class<? extends Object> caller, String message) {
PlatformLogger logger = PlatformLogger.getLogger(caller.getCanonicalName());
logger.config(message);
}
/**
* Lazy loaded set of available locales.
* Loading all locales is a very long operation.
*/
private static class AllAvailableLocales {
/**
* Available locales for all locale sensitive services.
* This also contains JRE's available locales
*/
static final Locale[] allAvailableLocales;
static {
Set<Locale> all = new HashSet<>();
for (Class<? extends LocaleServiceProvider> c : spiClasses) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(c);
all.addAll(pool.getAvailableLocaleSet());
}
allAvailableLocales = all.toArray(new Locale[0]);
}
// No instantiation
private 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
* provider, including the JRE.
*
* @return an array of the available locales for all provider classes
*/
public static Locale[] getAllAvailableLocales() {
return AllAvailableLocales.allAvailableLocales.clone();
}
/**
* Returns an array of available locales. This array is a
* merged array of all the locales that are provided by each
* provider, including the JRE.
*
* @return an array of the available locales
*/
public Locale[] getAvailableLocales() {
Set<Locale> locList = new HashSet<>();
locList.addAll(getAvailableLocaleSet());
// Make sure it all contains JRE's locales for compatibility.
locList.addAll(Arrays.asList(LocaleProviderAdapter.forJRE().getAvailableLocales()));
Locale[] tmp = new Locale[locList.size()];
locList.toArray(tmp);
return tmp;
}
/**
* Returns the union of locale sets that are available from
* each service provider. This method does NOT return the
* defensive copy.
*
* @return a set of available locales
*/
private synchronized Set<Locale> getAvailableLocaleSet() {
if (availableLocales == null) {
availableLocales = new HashSet<>();
for (LocaleServiceProvider lsp : providers.values()) {
Locale[] locales = lsp.getAvailableLocales();
for (Locale locale: locales) {
availableLocales.add(getLookupLocale(locale));
}
}
}
return availableLocales;
}
/**
* Returns whether any provider for this locale sensitive
* service is available or not, excluding JRE's one.
*
* @return true if any provider (other than JRE) is available
*/
boolean hasProviders() {
return providers.size() != 1 ||
(providers.get(LocaleProviderAdapter.Type.JRE) == null &&
providers.get(LocaleProviderAdapter.Type.FALLBACK) == null);
}
/**
* Returns the provider's localized object for the specified
* locale.
*
* @param getter an object on which getObject() method
* is called to obtain the provider's instance.
* @param locale the given locale that is used as the starting one
* @param params provider specific parameters
* @return provider's instance, or null.
*/
public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
Locale locale,
Object... params) {
return getLocalizedObjectImpl(getter, locale, true, null, params);
}
/**
* Returns the provider's localized name for the specified
* locale.
*
* @param getter an object on which getObject() method
* is called to obtain the provider's instance.
* @param locale the given locale that is used as the starting one
* @param key the key string for name providers
* @param params provider specific parameters
* @return provider's instance, or null.
*/
public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
Locale locale,
String key,
Object... params) {
return getLocalizedObjectImpl(getter, locale, false, key, params);
}
@SuppressWarnings("unchecked")
private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
Locale locale,
boolean isObjectProvider,
String key,
Object... params) {
if (locale == null) {
throw new NullPointerException();
}
// Check whether JRE is the sole locale data provider or not,
// and directly call it if it is.
if (!hasProviders()) {
return getter.getObject((P)providers.get(LocaleProviderAdapter.defaultLocaleProviderAdapter),
locale, key, params);
}
List<Locale> lookupLocales = getLookupLocales(locale);
Set<Locale> available = getAvailableLocaleSet();
for (Locale current : lookupLocales) {
if (available.contains(current)) {
S providersObj;
for (LocaleProviderAdapter.Type type: findProviders(current)) {
LocaleServiceProvider lsp = providers.get(type);
providersObj = getter.getObject((P)lsp, locale, key, params);
if (providersObj != null) {
return providersObj;
} else if (isObjectProvider) {
config(LocaleServiceProviderPool.class,
"A locale sensitive service provider returned null for a localized objects, which should not happen. provider: "
+ lsp + " locale: " + locale);
}
}
}
}
// not found.
return null;
}
/**
* Returns the list of locale service provider instances that support
* the specified locale.
*
* @param locale the given locale
* @return the list of locale data adapter types
*/
private List<LocaleProviderAdapter.Type> findProviders(Locale locale) {
List<LocaleProviderAdapter.Type> providersList = providersCache.get(locale);
if (providersList == null) {
for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {
LocaleServiceProvider lsp = providers.get(type);
if (lsp != null) {
if (lsp.isSupportedLocale(locale)) {
if (providersList == null) {
providersList = new ArrayList<>(2);
}
providersList.add(type);
}
}
}
if (providersList == null) {
providersList = NULL_LIST;
}
List<LocaleProviderAdapter.Type> val = providersCache.putIfAbsent(locale, providersList);
if (val != null) {
providersList = val;
}
}
return providersList;
}
/**
* Returns a list of candidate locales for service look up.
* @param locale the input locale
* @return the list of candidate locales for the given locale
*/
static List<Locale> getLookupLocales(Locale locale) {
// Note: We currently use the default implementation of
// ResourceBundle.Control.getCandidateLocales. The result
// returned by getCandidateLocales are already normalized
// (no extensions) for service look up.
List<Locale> lookupLocales = Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
.getCandidateLocales("", locale);
return lookupLocales;
}
/**
* Returns an instance of Locale used for service look up.
* The result Locale has no extensions except for ja_JP_JP
* and th_TH_TH
*
* @param locale the locale
* @return the locale used for service look up
*/
static Locale getLookupLocale(Locale locale) {
Locale lookupLocale = locale;
if (locale.hasExtensions()
&& !locale.equals(JRELocaleConstants.JA_JP_JP)
&& !locale.equals(JRELocaleConstants.TH_TH_TH)) {
// remove extensions
Builder locbld = new Builder();
try {
locbld.setLocale(locale);
locbld.clearExtensions();
lookupLocale = locbld.build();
} catch (IllformedLocaleException e) {
// A Locale with non-empty extensions
// should have well-formed fields except
// for ja_JP_JP and th_TH_TH. Therefore,
// it should never enter in this catch clause.
config(LocaleServiceProviderPool.class,
"A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
// Fallback - script field will be lost.
lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
}
}
return lookupLocale;
}
/**
* A dummy locale service provider list that indicates there is no
* provider available
*/
private static List<LocaleProviderAdapter.Type> NULL_LIST =
Collections.emptyList();
/**
* An interface to get a localized object for each locale sensitive
* service class.
*/
public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {
/**
* Returns an object from the provider
*
* @param lsp the provider
* @param locale the locale
* @param key key string to localize, or null if the provider is not
* a name provider
* @param params provider specific params
* @return localized object from the provider
*/
public S getObject(P lsp,
Locale locale,
String key,
Object... params);
}
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright (c) 1999, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.util.locale.provider;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.spi.NumberFormatProvider;
import java.util.Currency;
import java.util.Locale;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.NumberFormatProvider
* NumberFormatProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class NumberFormatProviderImpl extends NumberFormatProvider implements AvailableLanguageTags {
// Constants used by factory methods to specify a style of format.
private static final int NUMBERSTYLE = 0;
private static final int CURRENCYSTYLE = 1;
private static final int PERCENTSTYLE = 2;
private static final int SCIENTIFICSTYLE = 3;
private static final int INTEGERSTYLE = 4;
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public NumberFormatProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.forType(type).getAvailableLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>NumberFormat</code> instance which formats
* monetary values for the specified locale.
*
* @param locale the desired locale.
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a currency formatter
* @see java.text.NumberFormat#getCurrencyInstance(java.util.Locale)
*/
@Override
public NumberFormat getCurrencyInstance(Locale locale) {
return getInstance(locale, CURRENCYSTYLE);
}
/**
* Returns a new <code>NumberFormat</code> instance which formats
* integer values for the specified locale.
* The returned number format is configured to
* round floating point numbers to the nearest integer using
* half-even rounding (see {@link java.math.RoundingMode#HALF_EVEN HALF_EVEN})
* for formatting, and to parse only the integer part of
* an input string (see {@link
* java.text.NumberFormat#isParseIntegerOnly isParseIntegerOnly}).
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a number format for integer values
* @see java.text.NumberFormat#getIntegerInstance(java.util.Locale)
*/
@Override
public NumberFormat getIntegerInstance(Locale locale) {
return getInstance(locale, INTEGERSTYLE);
}
/**
* Returns a new general-purpose <code>NumberFormat</code> instance for
* the specified locale.
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a general-purpose number formatter
* @see java.text.NumberFormat#getNumberInstance(java.util.Locale)
*/
@Override
public NumberFormat getNumberInstance(Locale locale) {
return getInstance(locale, NUMBERSTYLE);
}
/**
* Returns a new <code>NumberFormat</code> instance which formats
* percentage values for the specified locale.
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a percent formatter
* @see java.text.NumberFormat#getPercentInstance(java.util.Locale)
*/
@Override
public NumberFormat getPercentInstance(Locale locale) {
return getInstance(locale, PERCENTSTYLE);
}
private NumberFormat getInstance(Locale locale,
int choice) {
if (locale == null) {
throw new NullPointerException();
}
LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
String[] numberPatterns = adapter.getLocaleResources(locale).getNumberPatterns();
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice;
DecimalFormat format = new DecimalFormat(numberPatterns[entry], symbols);
if (choice == INTEGERSTYLE) {
format.setMaximumFractionDigits(0);
format.setDecimalSeparatorAlwaysShown(false);
format.setParseIntegerOnly(true);
} else if (choice == CURRENCYSTYLE) {
adjustForCurrencyDefaultFractionDigits(format, symbols);
}
return format;
}
/**
* Adjusts the minimum and maximum fraction digits to values that
* are reasonable for the currency's default fraction digits.
*/
private static void adjustForCurrencyDefaultFractionDigits(
DecimalFormat format, DecimalFormatSymbols symbols) {
Currency currency = symbols.getCurrency();
if (currency == null) {
try {
currency = Currency.getInstance(symbols.getInternationalCurrencySymbol());
} catch (IllegalArgumentException e) {
}
}
if (currency != null) {
int digits = currency.getDefaultFractionDigits();
if (digits != -1) {
int oldMinDigits = format.getMinimumFractionDigits();
// Common patterns are "#.##", "#.00", "#".
// Try to adjust all of them in a reasonable way.
if (oldMinDigits == format.getMaximumFractionDigits()) {
format.setMinimumFractionDigits(digits);
format.setMaximumFractionDigits(digits);
} else {
format.setMinimumFractionDigits(Math.min(digits, oldMinDigits));
format.setMaximumFractionDigits(digits);
}
}
}
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import sun.util.resources.LocaleData;
/**
* Accessor for LocaleData
*
* @author Naoto Sato
*/
public interface ResourceBundleBasedAdapter {
public LocaleData getLocaleData();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,615 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.BreakIterator;
import java.text.Collator;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Locale;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
/**
* LocaleProviderAdapter implementation for the installed SPI implementations.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class SPILocaleProviderAdapter extends AuxLocaleProviderAdapter {
/**
* Returns the type of this LocaleProviderAdapter
*/
@Override
public LocaleProviderAdapter.Type getAdapterType() {
return LocaleProviderAdapter.Type.SPI;
}
@Override
protected <P extends LocaleServiceProvider> P findInstalledProvider(final Class<P> c) {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<P>() {
@Override
@SuppressWarnings("unchecked")
public P run() {
P delegate = null;
for (LocaleServiceProvider provider : ServiceLoader.loadInstalled(c)) {
if (delegate == null) {
try {
delegate =
(P) Class.forName(SPILocaleProviderAdapter.class.getCanonicalName() +
"$" +
c.getSimpleName() +
"Delegate")
.newInstance();
} catch (ClassNotFoundException |
InstantiationException |
IllegalAccessException e) {
LocaleServiceProviderPool.config(SPILocaleProviderAdapter.class, e.toString());
return null;
}
}
((Delegate)delegate).addImpl(provider);
}
return delegate;
}
});
} catch (PrivilegedActionException e) {
LocaleServiceProviderPool.config(SPILocaleProviderAdapter.class, e.toString());
}
return null;
}
/*
* Delegate interface. All the implementations have to have the class name
* following "<provider class name>Delegate" convention.
*/
interface Delegate<P extends LocaleServiceProvider> {
public void addImpl(P impl);
public P getImpl(Locale locale);
}
/*
* Obtain the real SPI implementation, using locale fallback
*/
private static <P extends LocaleServiceProvider> P getImpl(Map<Locale, P> map, Locale locale) {
for (Locale l : LocaleServiceProviderPool.getLookupLocales(locale)) {
P ret = map.get(l);
if (ret != null) {
return ret;
}
}
return null;
}
/*
* Delegates for the actual SPI implementations.
*/
static class BreakIteratorProviderDelegate extends BreakIteratorProvider
implements Delegate<BreakIteratorProvider> {
private ConcurrentMap<Locale, BreakIteratorProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(BreakIteratorProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public BreakIteratorProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public BreakIterator getWordInstance(Locale locale) {
BreakIteratorProvider bip = getImpl(locale);
assert bip != null;
return bip.getWordInstance(locale);
}
@Override
public BreakIterator getLineInstance(Locale locale) {
BreakIteratorProvider bip = getImpl(locale);
assert bip != null;
return bip.getLineInstance(locale);
}
@Override
public BreakIterator getCharacterInstance(Locale locale) {
BreakIteratorProvider bip = getImpl(locale);
assert bip != null;
return bip.getCharacterInstance(locale);
}
@Override
public BreakIterator getSentenceInstance(Locale locale) {
BreakIteratorProvider bip = getImpl(locale);
assert bip != null;
return bip.getSentenceInstance(locale);
}
}
static class CollatorProviderDelegate extends CollatorProvider implements Delegate<CollatorProvider> {
private ConcurrentMap<Locale, CollatorProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(CollatorProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public CollatorProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public Collator getInstance(Locale locale) {
CollatorProvider cp = getImpl(locale);
assert cp != null;
return cp.getInstance(locale);
}
}
static class DateFormatProviderDelegate extends DateFormatProvider
implements Delegate<DateFormatProvider> {
private ConcurrentMap<Locale, DateFormatProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(DateFormatProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public DateFormatProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public DateFormat getTimeInstance(int style, Locale locale) {
DateFormatProvider dfp = getImpl(locale);
assert dfp != null;
return dfp.getTimeInstance(style, locale);
}
@Override
public DateFormat getDateInstance(int style, Locale locale) {
DateFormatProvider dfp = getImpl(locale);
assert dfp != null;
return dfp.getDateInstance(style, locale);
}
@Override
public DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) {
DateFormatProvider dfp = getImpl(locale);
assert dfp != null;
return dfp.getDateTimeInstance(dateStyle, timeStyle, locale);
}
}
static class DateFormatSymbolsProviderDelegate extends DateFormatSymbolsProvider
implements Delegate<DateFormatSymbolsProvider> {
private ConcurrentMap<Locale, DateFormatSymbolsProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(DateFormatSymbolsProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public DateFormatSymbolsProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public DateFormatSymbols getInstance(Locale locale) {
DateFormatSymbolsProvider dfsp = getImpl(locale);
assert dfsp != null;
return dfsp.getInstance(locale);
}
}
static class DecimalFormatSymbolsProviderDelegate extends DecimalFormatSymbolsProvider
implements Delegate<DecimalFormatSymbolsProvider> {
private ConcurrentMap<Locale, DecimalFormatSymbolsProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(DecimalFormatSymbolsProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public DecimalFormatSymbolsProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public DecimalFormatSymbols getInstance(Locale locale) {
DecimalFormatSymbolsProvider dfsp = getImpl(locale);
assert dfsp != null;
return dfsp.getInstance(locale);
}
}
static class NumberFormatProviderDelegate extends NumberFormatProvider
implements Delegate<NumberFormatProvider> {
private ConcurrentMap<Locale, NumberFormatProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(NumberFormatProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public NumberFormatProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public NumberFormat getCurrencyInstance(Locale locale) {
NumberFormatProvider nfp = getImpl(locale);
assert nfp != null;
return nfp.getCurrencyInstance(locale);
}
@Override
public NumberFormat getIntegerInstance(Locale locale) {
NumberFormatProvider nfp = getImpl(locale);
assert nfp != null;
return nfp.getIntegerInstance(locale);
}
@Override
public NumberFormat getNumberInstance(Locale locale) {
NumberFormatProvider nfp = getImpl(locale);
assert nfp != null;
return nfp.getNumberInstance(locale);
}
@Override
public NumberFormat getPercentInstance(Locale locale) {
NumberFormatProvider nfp = getImpl(locale);
assert nfp != null;
return nfp.getPercentInstance(locale);
}
}
static class CalendarDataProviderDelegate extends CalendarDataProvider
implements Delegate<CalendarDataProvider> {
private ConcurrentMap<Locale, CalendarDataProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(CalendarDataProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public CalendarDataProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public int getFirstDayOfWeek(Locale locale) {
CalendarDataProvider cdp = getImpl(locale);
assert cdp != null;
return cdp.getFirstDayOfWeek(locale);
}
@Override
public int getMinimalDaysInFirstWeek(Locale locale) {
CalendarDataProvider cdp = getImpl(locale);
assert cdp != null;
return cdp.getMinimalDaysInFirstWeek(locale);
}
}
static class CalendarNameProviderDelegate extends CalendarNameProvider
implements Delegate<CalendarNameProvider> {
private ConcurrentMap<Locale, CalendarNameProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(CalendarNameProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public CalendarNameProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public String getDisplayName(String calendarType,
int field, int value,
int style, Locale locale) {
CalendarNameProvider cdp = getImpl(locale);
assert cdp != null;
return cdp.getDisplayName(calendarType, field, value, style, locale);
}
@Override
public Map<String, Integer> getDisplayNames(String calendarType,
int field, int style,
Locale locale) {
CalendarNameProvider cdp = getImpl(locale);
assert cdp != null;
return cdp.getDisplayNames(calendarType, field, style, locale);
}
}
static class CurrencyNameProviderDelegate extends CurrencyNameProvider
implements Delegate<CurrencyNameProvider> {
private ConcurrentMap<Locale, CurrencyNameProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(CurrencyNameProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public CurrencyNameProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public String getSymbol(String currencyCode, Locale locale) {
CurrencyNameProvider cnp = getImpl(locale);
assert cnp != null;
return cnp.getSymbol(currencyCode, locale);
}
@Override
public String getDisplayName(String currencyCode, Locale locale) {
CurrencyNameProvider cnp = getImpl(locale);
assert cnp != null;
return cnp.getDisplayName(currencyCode, locale);
}
}
static class LocaleNameProviderDelegate extends LocaleNameProvider
implements Delegate<LocaleNameProvider> {
private ConcurrentMap<Locale, LocaleNameProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(LocaleNameProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public LocaleNameProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public String getDisplayLanguage(String languageCode, Locale locale) {
LocaleNameProvider lnp = getImpl(locale);
assert lnp != null;
return lnp.getDisplayLanguage(languageCode, locale);
}
@Override
public String getDisplayScript(String scriptCode, Locale locale) {
LocaleNameProvider lnp = getImpl(locale);
assert lnp != null;
return lnp.getDisplayScript(scriptCode, locale);
}
@Override
public String getDisplayCountry(String countryCode, Locale locale) {
LocaleNameProvider lnp = getImpl(locale);
assert lnp != null;
return lnp.getDisplayCountry(countryCode, locale);
}
@Override
public String getDisplayVariant(String variant, Locale locale) {
LocaleNameProvider lnp = getImpl(locale);
assert lnp != null;
return lnp.getDisplayVariant(variant, locale);
}
}
static class TimeZoneNameProviderDelegate extends TimeZoneNameProvider
implements Delegate<TimeZoneNameProvider> {
private ConcurrentMap<Locale, TimeZoneNameProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(TimeZoneNameProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public TimeZoneNameProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
TimeZoneNameProvider tznp = getImpl(locale);
assert tznp != null;
return tznp.getDisplayName(ID, daylight, style, locale);
}
@Override
public String getGenericDisplayName(String ID, int style, Locale locale) {
TimeZoneNameProvider tznp = getImpl(locale);
assert tznp != null;
return tznp.getGenericDisplayName(ID, style, locale);
}
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.spi.TimeZoneNameProvider;
/**
* Concrete implementation of the
* {@link java.util.spi.TimeZoneNameProvider TimeZoneNameProvider} class
* for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class TimeZoneNameProviderImpl extends TimeZoneNameProvider {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
TimeZoneNameProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a name for the given time zone ID that's suitable for
* presentation to the user in the specified locale. The given time
* zone ID is "GMT" or one of the names defined using "Zone" entries
* in the "tz database", a public domain time zone database at
* <a href="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</a>.
* The data of this database is contained in a file whose name starts with
* "tzdata", and the specification of the data format is part of the zic.8
* man page, which is contained in a file whose name starts with "tzcode".
* <p>
* If <code>daylight</code> is true, the method should return a name
* appropriate for daylight saving time even if the specified time zone
* has not observed daylight saving time in the past.
*
* @param ID a time zone ID string
* @param daylight if true, return the daylight saving name.
* @param style either {@link java.util.TimeZone#LONG TimeZone.LONG} or
* {@link java.util.TimeZone#SHORT TimeZone.SHORT}
* @param locale the desired locale
* @return the human-readable name of the given time zone in the
* given locale, or null if it's not available.
* @exception IllegalArgumentException if <code>style</code> is invalid,
* or <code>locale</code> isn't one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>ID</code> or <code>locale</code>
* is null
* @see java.util.TimeZone#getDisplayName(boolean, int, java.util.Locale)
*/
@Override
public String getDisplayName(String id, boolean daylight, int style, Locale locale) {
String[] names = getDisplayNameArray(id, locale);
if (Objects.nonNull(names)) {
assert names.length >= 7;
int index = daylight ? 3 : 1;
if (style == TimeZone.SHORT) {
index++;
}
return names[index];
}
return null;
}
@Override
public String getGenericDisplayName(String id, int style, Locale locale) {
String[] names = getDisplayNameArray(id, locale);
if (Objects.nonNull(names)) {
assert names.length >= 7;
return names[(style == TimeZone.LONG) ? 5 : 6];
}
return null;
}
private String[] getDisplayNameArray(String id, Locale locale) {
Objects.requireNonNull(id);
Objects.requireNonNull(locale);
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id);
}
/**
* Returns a String[][] as the DateFormatSymbols.getZoneStrings() value for
* the given locale. This method is package private.
*
* @param locale a Locale for time zone names
* @return an array of time zone names arrays
*/
String[][] getZoneStrings(Locale locale) {
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getZoneStrings();
}
}

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2005, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 sun.util.locale.provider;
import java.lang.ref.SoftReference;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.spi.TimeZoneNameProvider;
import sun.util.calendar.ZoneInfo;
/**
* Utility class that deals with the localized time zone names
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public final class TimeZoneNameUtility {
/**
* cache to hold time zone localized strings. Keyed by Locale
*/
private static ConcurrentHashMap<Locale, SoftReference<String[][]>> cachedZoneData =
new ConcurrentHashMap<>();
/**
* Cache for managing display names per timezone per locale
* The structure is:
* Map(key=id, value=SoftReference(Map(key=locale, value=displaynames)))
*/
private static final Map<String, SoftReference<Map<Locale, String[]>>> cachedDisplayNames =
new ConcurrentHashMap<>();
/**
* get time zone localized strings. Enumerate all keys.
*/
public static String[][] getZoneStrings(Locale locale) {
String[][] zones;
SoftReference<String[][]> data = cachedZoneData.get(locale);
if (data == null || ((zones = data.get()) == null)) {
zones = loadZoneStrings(locale);
data = new SoftReference<>(zones);
cachedZoneData.put(locale, data);
}
return zones;
}
private static String[][] loadZoneStrings(Locale locale) {
// If the provider is a TimeZoneNameProviderImpl, call its getZoneStrings
// in order to avoid per-ID retrieval.
LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(TimeZoneNameProvider.class, locale);
TimeZoneNameProvider provider = adapter.getTimeZoneNameProvider();
if (provider instanceof TimeZoneNameProviderImpl) {
return ((TimeZoneNameProviderImpl)provider).getZoneStrings(locale);
}
// Performs per-ID retrieval.
Set<String> zoneIDs = LocaleProviderAdapter.forJRE().getLocaleResources(locale).getZoneIDs();
List<String[]> zones = new LinkedList<>();
for (String key : zoneIDs) {
String[] names = retrieveDisplayNamesImpl(key, locale);
if (names != null) {
zones.add(names);
}
}
String[][] zonesArray = new String[zones.size()][];
return zones.toArray(zonesArray);
}
/**
* Retrieve display names for a time zone ID.
*/
public static String[] retrieveDisplayNames(String id, Locale locale) {
Objects.requireNonNull(id);
Objects.requireNonNull(locale);
return retrieveDisplayNamesImpl(id, locale);
}
/**
* Retrieves a generic time zone display name for a time zone ID.
*
* @param id time zone ID
* @param style TimeZone.LONG or TimeZone.SHORT
* @param locale desired Locale
* @return the requested generic time zone display name, or null if not found.
*/
public static String retrieveGenericDisplayName(String id, int style, Locale locale) {
String[] names = retrieveDisplayNamesImpl(id, locale);
if (Objects.nonNull(names)) {
return names[6 - style];
} else {
return null;
}
}
/**
* Retrieves a standard or daylight-saving time name for the given time zone ID.
*
* @param id time zone ID
* @param daylight true for a daylight saving time name, or false for a standard time name
* @param style TimeZone.LONG or TimeZone.SHORT
* @param locale desired Locale
* @return the requested time zone name, or null if not found.
*/
public static String retrieveDisplayName(String id, boolean daylight, int style, Locale locale) {
String[] names = retrieveDisplayNamesImpl(id, locale);
if (Objects.nonNull(names)) {
return names[(daylight ? 4 : 2) - style];
} else {
return null;
}
}
private static String[] retrieveDisplayNamesImpl(String id, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
String[] names;
Map<Locale, String[]> perLocale = null;
SoftReference<Map<Locale, String[]>> ref = cachedDisplayNames.get(id);
if (Objects.nonNull(ref)) {
perLocale = ref.get();
if (Objects.nonNull(perLocale)) {
names = perLocale.get(locale);
if (Objects.nonNull(names)) {
return names;
}
}
}
// build names array
names = new String[7];
names[0] = id;
for (int i = 1; i <= 6; i ++) {
names[i] = pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale,
i<5 ? (i<3 ? "std" : "dst") : "generic", i%2, id);
}
if (Objects.isNull(perLocale)) {
perLocale = new ConcurrentHashMap<>();
}
perLocale.put(locale, names);
ref = new SoftReference<>(perLocale);
cachedDisplayNames.put(id, ref);
return names;
}
/**
* Obtains a localized time zone strings from a TimeZoneNameProvider
* implementation.
*/
private static class TimeZoneNameGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
String> {
private static final TimeZoneNameGetter INSTANCE =
new TimeZoneNameGetter();
@Override
public String getObject(TimeZoneNameProvider timeZoneNameProvider,
Locale locale,
String requestID,
Object... params) {
assert params.length == 2;
int style = (int) params[0];
String tzid = (String) params[1];
String value = getName(timeZoneNameProvider, locale, requestID, style, tzid);
if (value == null) {
Map<String, String> aliases = ZoneInfo.getAliasTable();
if (aliases != null) {
String canonicalID = aliases.get(tzid);
if (canonicalID != null) {
value = getName(timeZoneNameProvider, locale, requestID, style, canonicalID);
}
if (value == null) {
value = examineAliases(timeZoneNameProvider, locale, requestID,
canonicalID != null ? canonicalID : tzid, style, aliases);
}
}
}
return value;
}
private static String examineAliases(TimeZoneNameProvider tznp, Locale locale,
String requestID, String tzid, int style,
Map<String, String> aliases) {
for (Map.Entry<String, String> entry : aliases.entrySet()) {
if (entry.getValue().equals(tzid)) {
String alias = entry.getKey();
String name = getName(tznp, locale, requestID, style, alias);
if (name != null) {
return name;
}
name = examineAliases(tznp, locale, requestID, alias, style, aliases);
if (name != null) {
return name;
}
}
}
return null;
}
private static String getName(TimeZoneNameProvider timeZoneNameProvider,
Locale locale, String requestID, int style, String tzid) {
String value = null;
switch (requestID) {
case "std":
value = timeZoneNameProvider.getDisplayName(tzid, false, style, locale);
break;
case "dst":
value = timeZoneNameProvider.getDisplayName(tzid, true, style, locale);
break;
case "generic":
value = timeZoneNameProvider.getGenericDisplayName(tzid, style, locale);
break;
}
return value;
}
}
// No instantiation
private TimeZoneNameUtility() {
}
}