/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (ZoneNameProviderSPI.java) is part of project Time4J.
*
* Time4J is free software: You can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Time4J 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Time4J. If not, see <http://www.gnu.org/licenses/>.
* -----------------------------------------------------------------------
*/
package net.time4j.tz.spi;
import net.time4j.i18n.UTF8ResourceControl;
import net.time4j.tz.NameStyle;
import net.time4j.tz.TZID;
import net.time4j.tz.ZoneNameProvider;
import net.time4j.tz.olson.AFRICA;
import net.time4j.tz.olson.AMERICA;
import net.time4j.tz.olson.ANTARCTICA;
import net.time4j.tz.olson.ASIA;
import net.time4j.tz.olson.ATLANTIC;
import net.time4j.tz.olson.AUSTRALIA;
import net.time4j.tz.olson.EUROPE;
import net.time4j.tz.olson.INDIAN;
import net.time4j.tz.olson.PACIFIC;
import java.text.DateFormatSymbols;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* <p>Special implementation of {@code ZoneNameProvider} whose only purpose is
* to assist in resolving timezone names to ids. </p>
*
* @author Meno Hochschild
* @since 3.1
*/
public class ZoneNameProviderSPI
implements ZoneNameProvider {
//~ Statische Felder/Initialisierungen --------------------------------
private static final ConcurrentMap<Locale, Map<String, Map<NameStyle, String>>> NAMES = new ConcurrentHashMap<>();
private static final Set<String> GMT_ZONES;
private static final Map<String, Set<String>> TERRITORIES;
private static final Map<String, String> PRIMARIES;
private static final ResourceBundle.Control CONTROL;
static {
Set<String> gmtZones = new HashSet<>();
gmtZones.add("Z");
gmtZones.add("GMT");
gmtZones.add("GMT0");
gmtZones.add("Greenwich");
gmtZones.add("UCT");
gmtZones.add("UTC");
gmtZones.add("UTC0");
gmtZones.add("Universal");
gmtZones.add("Zulu");
GMT_ZONES = Collections.unmodifiableSet(gmtZones);
Map<String, Set<String>> temp = new HashMap<>();
for (AFRICA tzid : AFRICA.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
for (AMERICA tzid : AMERICA.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
for (TZID tzid : AMERICA.ARGENTINA.values()) {
addTerritory(temp, "AR", tzid);
}
for (TZID tzid : AMERICA.INDIANA.values()) {
addTerritory(temp, "US", tzid);
}
for (TZID tzid : AMERICA.KENTUCKY.values()) {
addTerritory(temp, "US", tzid);
}
for (TZID tzid : AMERICA.NORTH_DAKOTA.values()) {
addTerritory(temp, "US", tzid);
}
for (ANTARCTICA tzid : ANTARCTICA.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
for (ASIA tzid : ASIA.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
for (ATLANTIC tzid : ATLANTIC.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
for (AUSTRALIA tzid : AUSTRALIA.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
for (EUROPE tzid : EUROPE.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
for (INDIAN tzid : INDIAN.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
for (PACIFIC tzid : PACIFIC.values()) {
addTerritory(temp, tzid.getCountry(), tzid);
}
temp.put("SJ", Collections.singleton("Arctic/Longyearbyen"));
TERRITORIES = Collections.unmodifiableMap(temp);
// CLDR29 - supplemental\metaZones.xml - primaryZones
Map<String, String> primaries = new HashMap<>();
addPrimary(primaries, "CL", AMERICA.SANTIAGO);
addPrimary(primaries, "CN", ASIA.SHANGHAI);
addPrimary(primaries, "DE", EUROPE.BERLIN);
addPrimary(primaries, "EC", AMERICA.GUAYAQUIL);
addPrimary(primaries, "ES", EUROPE.MADRID);
addPrimary(primaries, "MH", PACIFIC.MAJURO);
addPrimary(primaries, "MY", ASIA.KUALA_LUMPUR);
addPrimary(primaries, "NZ", PACIFIC.AUCKLAND);
addPrimary(primaries, "PT", EUROPE.LISBON);
addPrimary(primaries, "UA", EUROPE.KIEV);
addPrimary(primaries, "UZ", ASIA.TASHKENT);
PRIMARIES = Collections.unmodifiableMap(primaries);
CONTROL =
new UTF8ResourceControl() {
protected String getModuleName() {
return "olson";
}
protected Class<?> getModuleRef() {
return ZoneNameProviderSPI.class;
}
};
}
//~ Methoden ----------------------------------------------------------
@Override
public Set<String> getPreferredIDs(
Locale locale,
boolean smart
) {
String country = locale.getCountry();
if (smart) {
if (country.equals("US")) {
Set<String> tzids = new LinkedHashSet<>();
tzids.add("America/New_York");
tzids.add("America/Chicago");
tzids.add("America/Denver");
tzids.add("America/Los_Angeles");
tzids.add("America/Anchorage");
tzids.add("Pacific/Honolulu");
tzids.add("America/Adak");
return Collections.unmodifiableSet(tzids);
} else {
String primaryZone = PRIMARIES.get(country);
if (primaryZone != null) {
return Collections.singleton(primaryZone);
}
}
}
Set<String> result = TERRITORIES.get(country);
if (result == null) {
result = Collections.emptySet();
}
return result;
}
@Override
public String getDisplayName(
String tzid,
NameStyle style,
Locale locale
) {
if (GMT_ZONES.contains(tzid)) {
return ""; // falls back to canonical identifier (Z for ZonalOffset.UTC)
}
Map<String, Map<NameStyle, String>> map = NAMES.get(locale);
if (map == null) {
DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
String[][] zoneNames = symbols.getZoneStrings();
map = new HashMap<>();
for (String[] arr : zoneNames) {
Map<NameStyle, String> names = new EnumMap<>(NameStyle.class);
names.put(NameStyle.LONG_STANDARD_TIME, arr[1]);
names.put(NameStyle.SHORT_STANDARD_TIME, arr[2]);
names.put(NameStyle.LONG_DAYLIGHT_TIME, arr[3]);
names.put(NameStyle.SHORT_DAYLIGHT_TIME, arr[4]);
map.put(arr[0], names);
}
Map<String, Map<NameStyle, String>> old = NAMES.putIfAbsent(locale, map);
if (old != null) {
map = old;
}
}
Map<NameStyle, String> styledNames = map.get(tzid);
if (styledNames != null) {
return styledNames.get(style);
}
return "";
// *************************************************************************************
// OLD CODE
// *************************************************************************************
// Timezone tz = Timezone.of("java.util.TimeZone~" + tzid, ZonalOffset.UTC);
//
// if (tz.isFixed() && tz.getOffset(Moment.UNIX_EPOCH).equals(ZonalOffset.UTC)) {
// return "";
// }
//
// return tz.getDisplayName(style, locale);
// *************************************************************************************
}
@Override
public String getStdFormatPattern(
boolean zeroOffset,
Locale locale
) {
return getBundle(locale).getString(zeroOffset ? "utc-literal" : "offset-pattern");
}
private static void addTerritory(
Map<String, Set<String>> map,
String country,
TZID tz
) {
Set<String> preferred = map.get(country);
if (preferred == null) {
preferred = new LinkedHashSet<>();
map.put(country, preferred);
}
preferred.add(tz.canonical());
}
private static void addPrimary(
Map<String, String> map,
String country,
TZID tz
) {
map.put(country, tz.canonical());
}
/**
* <p>Gets a resource bundle for given calendar type and locale. </p>
*
* @param desired locale (language and/or country)
* @return {@code ResourceBundle}
*/
private static ResourceBundle getBundle(Locale desired) {
return ResourceBundle.getBundle(
"zones/tzname",
desired,
ZoneNameProviderSPI.class.getClassLoader(),
CONTROL);
}
}