| @ -0,0 +1,194 @@ | |||||
| /* | |||||
| * Copyright (C) 2012 The Libphonenumber Authors | |||||
| * | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.google.i18n.phonenumbers; | |||||
| import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType; | |||||
| import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; | |||||
| import com.google.i18n.phonenumbers.prefixmapper.PrefixTimeZonesMap; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.io.ObjectInputStream; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Collections; | |||||
| import java.util.List; | |||||
| import java.util.logging.Level; | |||||
| import java.util.logging.Logger; | |||||
| /** | |||||
| * An offline mapper from phone numbers to time zones. | |||||
| * | |||||
| * @author Walter Erquinigo | |||||
| */ | |||||
| public class PhoneNumberToTimeZonesMapper { | |||||
| private static PhoneNumberToTimeZonesMapper instance = null; | |||||
| private static final String MAPPING_DATA_DIRECTORY = | |||||
| "/com/google/i18n/phonenumbers/timezones/data/"; | |||||
| private static final String MAPPING_DATA_FILE_NAME = "map_data"; | |||||
| // This is defined by ICU as the unknown time zone. | |||||
| private static final String UNKNOWN_TIMEZONE = "Etc/Unknown"; | |||||
| // A list with the ICU unknown time zone as single element. | |||||
| // @VisibleForTesting | |||||
| static final List<String> UNKNOWN_TIME_ZONE_LIST = new ArrayList<String>(1); | |||||
| static { | |||||
| UNKNOWN_TIME_ZONE_LIST.add(UNKNOWN_TIMEZONE); | |||||
| } | |||||
| private static final Logger LOGGER = | |||||
| Logger.getLogger(PhoneNumberToTimeZonesMapper.class.getName()); | |||||
| private PrefixTimeZonesMap prefixTimeZonesMap = null; | |||||
| // @VisibleForTesting | |||||
| PhoneNumberToTimeZonesMapper(String prefixTimeZonesMapDataDirectory) { | |||||
| this.prefixTimeZonesMap = loadPrefixTimeZonesMapFromFile( | |||||
| prefixTimeZonesMapDataDirectory + MAPPING_DATA_FILE_NAME); | |||||
| } | |||||
| private PhoneNumberToTimeZonesMapper(PrefixTimeZonesMap prefixTimeZonesMap) { | |||||
| this.prefixTimeZonesMap = prefixTimeZonesMap; | |||||
| } | |||||
| private static PrefixTimeZonesMap loadPrefixTimeZonesMapFromFile(String path) { | |||||
| InputStream source = PhoneNumberToTimeZonesMapper.class.getResourceAsStream(path); | |||||
| ObjectInputStream in = null; | |||||
| PrefixTimeZonesMap map = new PrefixTimeZonesMap(); | |||||
| try { | |||||
| in = new ObjectInputStream(source); | |||||
| map.readExternal(in); | |||||
| } catch (IOException e) { | |||||
| LOGGER.log(Level.WARNING, e.toString()); | |||||
| } finally { | |||||
| close(in); | |||||
| } | |||||
| return map; | |||||
| } | |||||
| private static void close(InputStream in) { | |||||
| if (in != null) { | |||||
| try { | |||||
| in.close(); | |||||
| } catch (IOException e) { | |||||
| LOGGER.log(Level.WARNING, e.toString()); | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Helper class used for lazy instantiation of a PhoneNumberToTimeZonesMapper. This also loads the | |||||
| * map data in a thread-safe way. | |||||
| */ | |||||
| private static class LazyHolder { | |||||
| private static final PhoneNumberToTimeZonesMapper INSTANCE; | |||||
| static { | |||||
| PrefixTimeZonesMap map = | |||||
| loadPrefixTimeZonesMapFromFile(MAPPING_DATA_DIRECTORY + MAPPING_DATA_FILE_NAME); | |||||
| INSTANCE = new PhoneNumberToTimeZonesMapper(map); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Gets a {@link PhoneNumberToTimeZonesMapper} instance. | |||||
| * | |||||
| * <p> The {@link PhoneNumberToTimeZonesMapper} is implemented as a singleton. Therefore, calling | |||||
| * this method multiple times will only result in one instance being created. | |||||
| * | |||||
| * @return a {@link PhoneNumberToTimeZonesMapper} instance | |||||
| */ | |||||
| public static synchronized PhoneNumberToTimeZonesMapper getInstance() { | |||||
| return LazyHolder.INSTANCE; | |||||
| } | |||||
| /** | |||||
| * Returns a list of time zones to which a phone number belongs. | |||||
| * | |||||
| * <p>This method assumes the validity of the number passed in has already been checked, and that | |||||
| * the number is geo-localizable. We consider fixed-line and mobile numbers possible candidates | |||||
| * for geo-localization. | |||||
| * | |||||
| * @param number a valid phone number for which we want to get the time zones to which it belongs | |||||
| * @return a list of the corresponding time zones or a single element list with the default | |||||
| * unknown time zone if no other time zone was found or if the number was invalid | |||||
| */ | |||||
| public List<String> getTimeZonesForGeographicalNumber(PhoneNumber number) { | |||||
| return getTimeZonesForGeocodableNumber(number); | |||||
| } | |||||
| /** | |||||
| * As per {@link #getTimeZonesForGeographicalNumber(PhoneNumber)} but explicitly checks | |||||
| * the validity of the number passed in. | |||||
| * | |||||
| * @param number the phone number for which we want to get the time zones to which it belongs | |||||
| * @return a list of the corresponding time zones or a single element list with the default | |||||
| * unknown time zone if no other time zone was found or if the number was invalid | |||||
| */ | |||||
| public List<String> getTimeZonesForNumber(PhoneNumber number) { | |||||
| PhoneNumberType numberType = PhoneNumberUtil.getInstance().getNumberType(number); | |||||
| if (numberType == PhoneNumberType.UNKNOWN) { | |||||
| return UNKNOWN_TIME_ZONE_LIST; | |||||
| } else if (!canBeGeocoded(numberType)) { | |||||
| return getCountryLevelTimeZonesforNumber(number); | |||||
| } | |||||
| return getTimeZonesForGeographicalNumber(number); | |||||
| } | |||||
| /** | |||||
| * A similar method is implemented as PhoneNumberUtil.isNumberGeographical, which performs a | |||||
| * stricter check, as it determines if a number has a geographical association. Also, if new | |||||
| * phone number types were added, we should check if this other method should be updated too. | |||||
| * TODO: Remove duplication by completing the logic in the method in PhoneNumberUtil. | |||||
| * For more information, see the comments in that method. | |||||
| */ | |||||
| private boolean canBeGeocoded(PhoneNumberType numberType) { | |||||
| return (numberType == PhoneNumberType.FIXED_LINE || | |||||
| numberType == PhoneNumberType.MOBILE || | |||||
| numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE); | |||||
| } | |||||
| /** | |||||
| * Returns a String with the ICU unknown time zone. | |||||
| */ | |||||
| public static String getUnknownTimeZone() { | |||||
| return UNKNOWN_TIMEZONE; | |||||
| } | |||||
| /** | |||||
| * Returns a list of time zones to which a geocodable phone number belongs. | |||||
| * | |||||
| * @param number the phone number for which we want to get the time zones to which it belongs | |||||
| * @return the list of corresponding time zones or a single element list with the default | |||||
| * unknown time zone if no other time zone was found or if the number was invalid | |||||
| */ | |||||
| private List<String> getTimeZonesForGeocodableNumber(PhoneNumber number) { | |||||
| List<String> timezones = prefixTimeZonesMap.lookupTimeZonesForNumber(number); | |||||
| return Collections.unmodifiableList(timezones.isEmpty() ? UNKNOWN_TIME_ZONE_LIST | |||||
| : timezones); | |||||
| } | |||||
| /** | |||||
| * Returns the list of time zones corresponding to the country calling code of {@code number}. | |||||
| * | |||||
| * @param number the phone number to look up | |||||
| * @return the list of corresponding time zones or a single element list with the default | |||||
| * unknown time zone if no other time zone was found | |||||
| */ | |||||
| private List<String> getCountryLevelTimeZonesforNumber(PhoneNumber number) { | |||||
| List<String> timezones = prefixTimeZonesMap.lookupCountryLevelTimeZonesForNumber(number); | |||||
| return Collections.unmodifiableList(timezones.isEmpty() ? UNKNOWN_TIME_ZONE_LIST | |||||
| : timezones); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,136 @@ | |||||
| /* | |||||
| * Copyright (C) 2012 The Libphonenumber Authors | |||||
| * | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.google.i18n.phonenumbers; | |||||
| import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; | |||||
| import junit.framework.TestCase; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| /** | |||||
| * Unit tests for PhoneNumberToTimeZonesMapper.java | |||||
| * | |||||
| * @author Walter Erquinigo | |||||
| */ | |||||
| public class PhoneNumberToTimeZonesMapperTest extends TestCase { | |||||
| private final PhoneNumberToTimeZonesMapper prefixTimeZonesMapper = | |||||
| new PhoneNumberToTimeZonesMapper(TEST_MAPPING_DATA_DIRECTORY); | |||||
| private static final String TEST_MAPPING_DATA_DIRECTORY = | |||||
| "/com/google/i18n/phonenumbers/timezones/testing_data/"; | |||||
| // Set up some test numbers to re-use. | |||||
| private static final PhoneNumber AU_NUMBER = | |||||
| new PhoneNumber().setCountryCode(61).setNationalNumber(236618300L); | |||||
| private static final PhoneNumber CA_NUMBER = | |||||
| new PhoneNumber().setCountryCode(1).setNationalNumber(6048406565L); | |||||
| private static final PhoneNumber KO_NUMBER = | |||||
| new PhoneNumber().setCountryCode(82).setNationalNumber(22123456L); | |||||
| private static final PhoneNumber KO_INVALID_NUMBER = | |||||
| new PhoneNumber().setCountryCode(82).setNationalNumber(1234L); | |||||
| private static final PhoneNumber US_NUMBER1 = | |||||
| new PhoneNumber().setCountryCode(1).setNationalNumber(6509600000L); | |||||
| private static final PhoneNumber US_NUMBER2 = | |||||
| new PhoneNumber().setCountryCode(1).setNationalNumber(2128120000L); | |||||
| private static final PhoneNumber US_NUMBER3 = | |||||
| new PhoneNumber().setCountryCode(1).setNationalNumber(6174240000L); | |||||
| private static final PhoneNumber US_INVALID_NUMBER = | |||||
| new PhoneNumber().setCountryCode(1).setNationalNumber(123456789L); | |||||
| private static final PhoneNumber NUMBER_WITH_INVALID_COUNTRY_CODE = | |||||
| new PhoneNumber().setCountryCode(999).setNationalNumber(2423651234L); | |||||
| private static final PhoneNumber INTERNATIONAL_TOLL_FREE = | |||||
| new PhoneNumber().setCountryCode(800).setNationalNumber(12345678L); | |||||
| // NANPA time zones. | |||||
| private static final String CHICAGO_TZ = "America/Chicago"; | |||||
| private static final String LOS_ANGELES_TZ = "America/Los_Angeles"; | |||||
| private static final String NEW_YORK_TZ = "America/New_York"; | |||||
| private static final String WINNIPEG_TZ = "America/Winnipeg"; | |||||
| // Non NANPA time zones. | |||||
| private static final String SEOUL_TZ = "Asia/Seoul"; | |||||
| private static final String SYDNEY_TZ = "Australia/Sydney"; | |||||
| static List<String> buildListOfTimeZones(String ... timezones) { | |||||
| ArrayList<String> timezonesList = new ArrayList<String>(timezones.length); | |||||
| for (String timezone : timezones) { | |||||
| timezonesList.add(timezone); | |||||
| } | |||||
| return timezonesList; | |||||
| } | |||||
| private static List<String> getNanpaTimeZonesList() { | |||||
| return buildListOfTimeZones(NEW_YORK_TZ, CHICAGO_TZ, WINNIPEG_TZ, LOS_ANGELES_TZ); | |||||
| } | |||||
| public void testGetTimeZonesForNumber() { | |||||
| // Test with invalid numbers even when their country code prefixes exist in the mapper. | |||||
| assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(US_INVALID_NUMBER)); | |||||
| assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(KO_INVALID_NUMBER)); | |||||
| // Test with valid prefixes. | |||||
| assertEquals(buildListOfTimeZones(SYDNEY_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(AU_NUMBER)); | |||||
| assertEquals(buildListOfTimeZones(SEOUL_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(KO_NUMBER)); | |||||
| assertEquals(buildListOfTimeZones(WINNIPEG_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(CA_NUMBER)); | |||||
| assertEquals(buildListOfTimeZones(LOS_ANGELES_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(US_NUMBER1)); | |||||
| assertEquals(buildListOfTimeZones(NEW_YORK_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(US_NUMBER2)); | |||||
| // Test with an invalid country code. | |||||
| assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(NUMBER_WITH_INVALID_COUNTRY_CODE)); | |||||
| // Test with a non geographical phone number. | |||||
| assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, | |||||
| prefixTimeZonesMapper.getTimeZonesForNumber(INTERNATIONAL_TOLL_FREE)); | |||||
| } | |||||
| public void testGetTimeZonesForValidNumber() { | |||||
| // Test with invalid numbers even when their country code prefixes exist in the mapper. | |||||
| assertEquals(getNanpaTimeZonesList(), | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(US_INVALID_NUMBER)); | |||||
| assertEquals(buildListOfTimeZones(SEOUL_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(KO_INVALID_NUMBER)); | |||||
| // Test with valid prefixes. | |||||
| assertEquals(buildListOfTimeZones(SYDNEY_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(AU_NUMBER)); | |||||
| assertEquals(buildListOfTimeZones(SEOUL_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(KO_NUMBER)); | |||||
| assertEquals(buildListOfTimeZones(WINNIPEG_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(CA_NUMBER)); | |||||
| assertEquals(buildListOfTimeZones(LOS_ANGELES_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(US_NUMBER1)); | |||||
| assertEquals(buildListOfTimeZones(NEW_YORK_TZ), | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(US_NUMBER2)); | |||||
| // Test with an invalid country code. | |||||
| assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber( | |||||
| NUMBER_WITH_INVALID_COUNTRY_CODE)); | |||||
| // Test with a non geographical phone number. | |||||
| assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, | |||||
| prefixTimeZonesMapper.getTimeZonesForGeographicalNumber( | |||||
| INTERNATIONAL_TOLL_FREE)); | |||||
| } | |||||
| public void testGetTimeZonesForValidNumberSearchingAtCountryCodeLevel() { | |||||
| // Test that the country level time zones are returned when the number passed in is valid but | |||||
| // not covered by any non-country level prefixes in the mapper. | |||||
| assertEquals(prefixTimeZonesMapper.getTimeZonesForNumber(US_NUMBER3), | |||||
| getNanpaTimeZonesList()); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,128 @@ | |||||
| /* | |||||
| * Copyright (C) 2012 The Libphonenumber Authors | |||||
| * | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.google.i18n.phonenumbers.prefixmapper; | |||||
| import com.google.i18n.phonenumbers.PhoneNumberUtil; | |||||
| import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; | |||||
| import java.io.Externalizable; | |||||
| import java.io.IOException; | |||||
| import java.io.ObjectInput; | |||||
| import java.io.ObjectOutput; | |||||
| import java.util.LinkedList; | |||||
| import java.util.List; | |||||
| import java.util.SortedMap; | |||||
| import java.util.StringTokenizer; | |||||
| /** | |||||
| * A utility that maps phone number prefixes to a list of strings describing the time zones to | |||||
| * which each prefix belongs. | |||||
| * | |||||
| * @author Walter Erquinigo | |||||
| */ | |||||
| public class PrefixTimeZonesMap implements Externalizable { | |||||
| private final PhonePrefixMap phonePrefixMap = new PhonePrefixMap(); | |||||
| private static final String RAW_STRING_TIMEZONES_SEPARATOR = "&"; | |||||
| /** | |||||
| * Creates a {@link PrefixTimeZoneMap} initialized with {@code sortedPrefixTimeZoneMap}. Note | |||||
| * that the underlying implementation of this method is expensive thus should not be called by | |||||
| * time-critical applications. | |||||
| * | |||||
| * @param sortedPrefixTimeZoneMap a map from phone number prefixes to their corresponding time | |||||
| * zones, sorted in ascending order of the phone number prefixes as integers. | |||||
| */ | |||||
| public void readPrefixTimeZonesMap(SortedMap<Integer, String> sortedPrefixTimeZoneMap) { | |||||
| phonePrefixMap.readPhonePrefixMap(sortedPrefixTimeZoneMap); | |||||
| } | |||||
| /** | |||||
| * Supports Java Serialization. | |||||
| */ | |||||
| public void writeExternal(ObjectOutput objectOutput) throws IOException { | |||||
| phonePrefixMap.writeExternal(objectOutput); | |||||
| } | |||||
| public void readExternal(ObjectInput objectInput) throws IOException { | |||||
| phonePrefixMap.readExternal(objectInput); | |||||
| } | |||||
| /** | |||||
| * Returns the list of time zones {@code key} corresponds to. | |||||
| * | |||||
| * <p>{@code key} could be the calling country code and the full significant number of a | |||||
| * certain number, or it could be just a phone-number prefix. | |||||
| * For example, the full number 16502530000 (from the phone-number +1 650 253 0000) is a valid | |||||
| * input. Also, any of its prefixes, such as 16502, is also valid. | |||||
| * | |||||
| * @param key the key to look up | |||||
| * @return the list of corresponding time zones | |||||
| */ | |||||
| private List<String> lookupTimeZonesForNumber(long key) { | |||||
| // Lookup in the map data. The returned String may consist of several time zones, so it must be | |||||
| // split. | |||||
| String timezonesString = phonePrefixMap.lookup(key); | |||||
| if (timezonesString == null) { | |||||
| return new LinkedList<String>(); | |||||
| } | |||||
| return tokenizeRawOutputString(timezonesString); | |||||
| } | |||||
| /** | |||||
| * As per {@link #lookupTimeZonesForNumber(long)}, but receives the number as a PhoneNumber | |||||
| * instead of a long. | |||||
| * | |||||
| * @param number the phone number to look up | |||||
| * @return the list of corresponding time zones | |||||
| */ | |||||
| public List<String> lookupTimeZonesForNumber(PhoneNumber number) { | |||||
| long phonePrefix = Long.parseLong(number.getCountryCode() + | |||||
| PhoneNumberUtil.getInstance().getNationalSignificantNumber(number)); | |||||
| return lookupTimeZonesForNumber(phonePrefix); | |||||
| } | |||||
| /** | |||||
| * Returns the list of time zones {@code number}'s calling country code corresponds to. | |||||
| * | |||||
| * @param number the phone number to look up | |||||
| * @return the list of corresponding time zones | |||||
| */ | |||||
| public List<String> lookupCountryLevelTimeZonesForNumber(PhoneNumber number) { | |||||
| return lookupTimeZonesForNumber(number.getCountryCode()); | |||||
| } | |||||
| /** | |||||
| * Split {@code timezonesString} into all the time zones that are part of it. | |||||
| */ | |||||
| private List<String> tokenizeRawOutputString(String timezonesString) { | |||||
| StringTokenizer tokenizer = new StringTokenizer(timezonesString, | |||||
| RAW_STRING_TIMEZONES_SEPARATOR); | |||||
| LinkedList<String> timezonesList = new LinkedList<String>(); | |||||
| while (tokenizer.hasMoreTokens()) { | |||||
| timezonesList.add(tokenizer.nextToken()); | |||||
| } | |||||
| return timezonesList; | |||||
| } | |||||
| /** | |||||
| * Dumps the mappings contained in the phone prefix map. | |||||
| */ | |||||
| @Override | |||||
| public String toString() { | |||||
| return phonePrefixMap.toString(); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,178 @@ | |||||
| /* | |||||
| * Copyright (C) 2012 The Libphonenumber Authors | |||||
| * | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.google.i18n.phonenumbers.prefixmapper; | |||||
| import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; | |||||
| import junit.framework.TestCase; | |||||
| import java.io.ByteArrayInputStream; | |||||
| import java.io.ByteArrayOutputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.ObjectInputStream; | |||||
| import java.io.ObjectOutputStream; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| import java.util.SortedMap; | |||||
| import java.util.TreeMap; | |||||
| /** | |||||
| * Unittests for PrefixTimeZonesMap.java | |||||
| * | |||||
| * @author Walter Erquinigo | |||||
| */ | |||||
| public class PrefixTimeZonesMapTest extends TestCase { | |||||
| private final PrefixTimeZonesMap prefixTimeZonesMapForUS = new PrefixTimeZonesMap(); | |||||
| private final PrefixTimeZonesMap prefixTimeZonesMapForRU = new PrefixTimeZonesMap(); | |||||
| // US time zones | |||||
| private static final String CHICAGO_TZ = "America/Chicago"; | |||||
| private static final String DENVER_TZ = "America/Denver"; | |||||
| private static final String LOS_ANGELES_TZ = "America/Los_Angeles"; | |||||
| private static final String NEW_YORK_TZ = "America/New_York"; | |||||
| // Russian time zones | |||||
| private static final String IRKUTSK_TZ = "Asia/Irkutsk"; | |||||
| private static final String MOSCOW_TZ = "Europe/Moscow"; | |||||
| private static final String VLADIVOSTOK_TZ = "Asia/Vladivostok"; | |||||
| private static final String YEKATERINBURG_TZ = "Asia/Yekaterinburg"; | |||||
| public PrefixTimeZonesMapTest() { | |||||
| SortedMap<Integer, String> sortedMapForUS = new TreeMap<Integer, String>(); | |||||
| sortedMapForUS.put(1, NEW_YORK_TZ + "&" + CHICAGO_TZ + "&" + LOS_ANGELES_TZ + "&" + DENVER_TZ); | |||||
| sortedMapForUS.put(1201, NEW_YORK_TZ); | |||||
| sortedMapForUS.put(1205, CHICAGO_TZ); | |||||
| sortedMapForUS.put(1208292, LOS_ANGELES_TZ); | |||||
| sortedMapForUS.put(1208234, DENVER_TZ); | |||||
| sortedMapForUS.put(1541367, LOS_ANGELES_TZ); | |||||
| sortedMapForUS.put(1423843, NEW_YORK_TZ); | |||||
| sortedMapForUS.put(1402721, CHICAGO_TZ); | |||||
| sortedMapForUS.put(1208888, DENVER_TZ); | |||||
| prefixTimeZonesMapForUS.readPrefixTimeZonesMap(sortedMapForUS); | |||||
| SortedMap<Integer, String> sortedMapForRU = new TreeMap<Integer, String>(); | |||||
| sortedMapForRU.put(7421, VLADIVOSTOK_TZ); | |||||
| sortedMapForRU.put(7879, MOSCOW_TZ); | |||||
| sortedMapForRU.put(7342, YEKATERINBURG_TZ); | |||||
| sortedMapForRU.put(7395, IRKUTSK_TZ); | |||||
| prefixTimeZonesMapForRU.readPrefixTimeZonesMap(sortedMapForRU); | |||||
| } | |||||
| static List<String> buildListOfTimeZones(String ... timezones) { | |||||
| ArrayList<String> timezonesList = new ArrayList<String>(timezones.length); | |||||
| for (String timezone : timezones) { | |||||
| timezonesList.add(timezone); | |||||
| } | |||||
| return timezonesList; | |||||
| } | |||||
| private static SortedMap<Integer, String> createMapCandidate() { | |||||
| SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>(); | |||||
| sortedMap.put(1212, NEW_YORK_TZ); | |||||
| sortedMap.put(1213, NEW_YORK_TZ); | |||||
| sortedMap.put(1214, NEW_YORK_TZ); | |||||
| sortedMap.put(1480, CHICAGO_TZ); | |||||
| return sortedMap; | |||||
| } | |||||
| public void testLookupTimeZonesForNumberCountryLevel_US() { | |||||
| PhoneNumber number = new PhoneNumber(); | |||||
| number.setCountryCode(1).setNationalNumber(1000000000L); | |||||
| assertEquals(buildListOfTimeZones(NEW_YORK_TZ, CHICAGO_TZ, LOS_ANGELES_TZ, DENVER_TZ), | |||||
| prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); | |||||
| } | |||||
| public void testLookupTimeZonesForNumber_ValidNumber_Chicago() { | |||||
| PhoneNumber number = new PhoneNumber(); | |||||
| number.setCountryCode(1).setNationalNumber(2051235458L); | |||||
| assertEquals(buildListOfTimeZones(CHICAGO_TZ), | |||||
| prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); | |||||
| } | |||||
| public void testLookupTimeZonesForNumber_LA() { | |||||
| PhoneNumber number = new PhoneNumber(); | |||||
| number.setCountryCode(1).setNationalNumber(2082924565L); | |||||
| assertEquals(buildListOfTimeZones(LOS_ANGELES_TZ), | |||||
| prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); | |||||
| } | |||||
| public void testLookupTimeZonesForNumber_NY() { | |||||
| PhoneNumber number = new PhoneNumber(); | |||||
| number.setCountryCode(1).setNationalNumber(2016641234L); | |||||
| assertEquals(buildListOfTimeZones(NEW_YORK_TZ), | |||||
| prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); | |||||
| } | |||||
| public void testLookupTimeZonesForNumber_CH() { | |||||
| PhoneNumber number = new PhoneNumber(); | |||||
| number.setCountryCode(41).setNationalNumber(446681300L); | |||||
| assertEquals(buildListOfTimeZones(), | |||||
| prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); | |||||
| } | |||||
| public void testLookupTimeZonesForNumber_RU() { | |||||
| PhoneNumber number = new PhoneNumber(); | |||||
| number.setCountryCode(7).setNationalNumber(87945154L); | |||||
| assertEquals(buildListOfTimeZones(MOSCOW_TZ), | |||||
| prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); | |||||
| number.setNationalNumber(421548578L); | |||||
| assertEquals(buildListOfTimeZones(VLADIVOSTOK_TZ), | |||||
| prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); | |||||
| number.setNationalNumber(342457897L); | |||||
| assertEquals(buildListOfTimeZones(YEKATERINBURG_TZ), | |||||
| prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); | |||||
| // A mobile number | |||||
| number.setNationalNumber(9342457897L); | |||||
| assertEquals(buildListOfTimeZones(), | |||||
| prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); | |||||
| // An invalid number (too short) | |||||
| number.setNationalNumber(3951L); | |||||
| assertEquals(buildListOfTimeZones(IRKUTSK_TZ), | |||||
| prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); | |||||
| } | |||||
| /** | |||||
| * Creates a new PrefixTimeZonesMap serializing the provided map to a stream and then reading | |||||
| * this stream. The resulting PrefixTimeZonesMap is expected to be strictly equal to the provided | |||||
| * one from which it was generated. | |||||
| */ | |||||
| private static PrefixTimeZonesMap createNewPrefixTimeZonesMap( | |||||
| PrefixTimeZonesMap prefixTimeZonesMap) throws IOException { | |||||
| ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | |||||
| ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); | |||||
| prefixTimeZonesMap.writeExternal(objectOutputStream); | |||||
| objectOutputStream.flush(); | |||||
| PrefixTimeZonesMap newPrefixTimeZonesMap = new PrefixTimeZonesMap(); | |||||
| newPrefixTimeZonesMap.readExternal( | |||||
| new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()))); | |||||
| return newPrefixTimeZonesMap; | |||||
| } | |||||
| public void testReadWriteExternal() throws IOException { | |||||
| PrefixTimeZonesMap localPrefixTimeZonesMap = new PrefixTimeZonesMap(); | |||||
| localPrefixTimeZonesMap.readPrefixTimeZonesMap(createMapCandidate()); | |||||
| PrefixTimeZonesMap newPrefixTimeZonesMap = createNewPrefixTimeZonesMap(localPrefixTimeZonesMap); | |||||
| assertEquals(localPrefixTimeZonesMap.toString(), newPrefixTimeZonesMap.toString()); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,24 @@ | |||||
| # Copyright (C) 2012 The Libphonenumber Authors | |||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| # you may not use this file except in compliance with the License. | |||||
| # You may obtain a copy of the License at | |||||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||||
| # Unless required by applicable law or agreed to in writing, software | |||||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| # See the License for the specific language governing permissions and | |||||
| # limitations under the License. | |||||
| 1|America/New_York&America/Chicago&America/Winnipeg&America/Los_Angeles | |||||
| 1201|America/New_York | |||||
| 1212812|America/New_York | |||||
| 1234|America/New_York | |||||
| 1604|America/Winnipeg | |||||
| 1617423|America/Chicago | |||||
| 1650960|America/Los_Angeles | |||||
| 1989|Ameriac/Los_Angeles | |||||
| 612|Australia/Sydney | |||||
| 82|Asia/Seoul | |||||
| @ -0,0 +1,143 @@ | |||||
| /* | |||||
| * Copyright (C) 2012 The Libphonenumber Authors | |||||
| * | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.google.i18n.phonenumbers.buildtools; | |||||
| import com.google.i18n.phonenumbers.prefixmapper.PrefixTimeZonesMap; | |||||
| import java.io.BufferedInputStream; | |||||
| import java.io.BufferedReader; | |||||
| import java.io.File; | |||||
| import java.io.FileInputStream; | |||||
| import java.io.FileOutputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.io.InputStreamReader; | |||||
| import java.io.ObjectOutputStream; | |||||
| import java.io.OutputStream; | |||||
| import java.nio.charset.Charset; | |||||
| import java.util.SortedMap; | |||||
| import java.util.TreeMap; | |||||
| import java.util.logging.Level; | |||||
| import java.util.logging.Logger; | |||||
| /** | |||||
| * A utility that generates the binary serialization of the prefix/time zones mappings from | |||||
| * a human-readable text file. | |||||
| * | |||||
| * @author Walter Erquinigo | |||||
| */ | |||||
| public class GenerateTimeZonesMapData { | |||||
| private final File inputTextFile; | |||||
| private static final String MAPPING_DATA_FILE_NAME = "map_data"; | |||||
| // The IO Handler used to output the generated binary file. | |||||
| private final AbstractPhonePrefixDataIOHandler ioHandler; | |||||
| private static final Logger LOGGER = Logger.getLogger(GenerateTimeZonesMapData.class.getName()); | |||||
| public GenerateTimeZonesMapData(File inputTextFile, AbstractPhonePrefixDataIOHandler ioHandler) | |||||
| throws IOException { | |||||
| this.inputTextFile = inputTextFile; | |||||
| if (!inputTextFile.isFile()) { | |||||
| throw new IOException("The provided input text file does not exist."); | |||||
| } | |||||
| this.ioHandler = ioHandler; | |||||
| } | |||||
| /** | |||||
| * Reads phone prefix data from the provided input stream and returns a SortedMap with the | |||||
| * prefix to time zones mappings. | |||||
| */ | |||||
| // @VisibleForTesting | |||||
| static SortedMap<Integer, String> parseTextFile(InputStream input) | |||||
| throws IOException, RuntimeException { | |||||
| final SortedMap<Integer, String> timeZoneMap = new TreeMap<Integer, String>(); | |||||
| BufferedReader bufferedReader = | |||||
| new BufferedReader(new InputStreamReader( | |||||
| new BufferedInputStream(input), Charset.forName("UTF-8"))); | |||||
| int lineNumber = 1; | |||||
| for (String line; (line = bufferedReader.readLine()) != null; lineNumber++) { | |||||
| line = line.trim(); | |||||
| if (line.length() == 0 || line.startsWith("#")) { | |||||
| continue; | |||||
| } | |||||
| int indexOfPipe = line.indexOf('|'); | |||||
| if (indexOfPipe == -1) { | |||||
| throw new RuntimeException(String.format("line %d: malformatted data, expected '|'", | |||||
| lineNumber)); | |||||
| } | |||||
| Integer prefix = Integer.parseInt(line.substring(0, indexOfPipe)); | |||||
| String timezones = line.substring(indexOfPipe + 1); | |||||
| if (timezones.isEmpty()) { | |||||
| throw new RuntimeException(String.format("line %d: missing time zones", lineNumber)); | |||||
| } | |||||
| if (timeZoneMap.put(prefix, timezones) != null) { | |||||
| throw new RuntimeException(String.format("duplicated prefix %d", prefix)); | |||||
| } | |||||
| } | |||||
| return timeZoneMap; | |||||
| } | |||||
| /** | |||||
| * Writes the provided phone prefix/time zones map to the provided output stream. | |||||
| * | |||||
| * @throws IOException | |||||
| */ | |||||
| // @VisibleForTesting | |||||
| static void writeToBinaryFile(SortedMap<Integer, String> sortedMap, OutputStream output) | |||||
| throws IOException { | |||||
| // Build the corresponding PrefixTimeZonesMap and serialize it to the binary format. | |||||
| PrefixTimeZonesMap prefixTimeZonesMap = new PrefixTimeZonesMap(); | |||||
| prefixTimeZonesMap.readPrefixTimeZonesMap(sortedMap); | |||||
| ObjectOutputStream objectOutputStream = new ObjectOutputStream(output); | |||||
| prefixTimeZonesMap.writeExternal(objectOutputStream); | |||||
| objectOutputStream.flush(); | |||||
| } | |||||
| /** | |||||
| * Runs the prefix to time zones map data generator. | |||||
| * | |||||
| * @throws IOException | |||||
| */ | |||||
| public void run() throws IOException { | |||||
| FileInputStream fileInputStream = null; | |||||
| FileOutputStream fileOutputStream = null; | |||||
| try { | |||||
| fileInputStream = new FileInputStream(inputTextFile); | |||||
| SortedMap<Integer, String> mappings = parseTextFile(fileInputStream); | |||||
| File outputBinaryFile = ioHandler.createFile(MAPPING_DATA_FILE_NAME); | |||||
| try { | |||||
| fileOutputStream = new FileOutputStream(outputBinaryFile); | |||||
| writeToBinaryFile(mappings, fileOutputStream); | |||||
| ioHandler.addFileToOutput(outputBinaryFile); | |||||
| } finally { | |||||
| ioHandler.closeFile(fileOutputStream); | |||||
| } | |||||
| } catch (RuntimeException e) { | |||||
| LOGGER.log(Level.SEVERE, | |||||
| "Error processing file " + inputTextFile.getAbsolutePath()); | |||||
| throw e; | |||||
| } catch (IOException e) { | |||||
| LOGGER.log(Level.SEVERE, e.getMessage()); | |||||
| } finally { | |||||
| ioHandler.closeFile(fileInputStream); | |||||
| ioHandler.closeFile(fileOutputStream); | |||||
| ioHandler.close(); | |||||
| } | |||||
| LOGGER.log(Level.INFO, "Time zone data successfully generated."); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,60 @@ | |||||
| /* | |||||
| * Copyright (C) 2012 The Libphonenumber Authors | |||||
| * | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.google.i18n.phonenumbers.buildtools; | |||||
| import com.google.i18n.phonenumbers.Command; | |||||
| import java.io.File; | |||||
| import java.io.IOException; | |||||
| import java.util.logging.Level; | |||||
| import java.util.logging.Logger; | |||||
| /** | |||||
| * Entry point class used to invoke the generation of the binary time zone data files. | |||||
| * | |||||
| * @author Walter Erquinigo | |||||
| * @author Philippe Liard | |||||
| */ | |||||
| public class GenerateTimeZonesMapDataEntryPoint extends Command { | |||||
| private static final Logger LOGGER = Logger.getLogger(GenerateTimeZonesMapData.class.getName()); | |||||
| @Override | |||||
| public String getCommandName() { | |||||
| return "GenerateTimeZonesMapData"; | |||||
| } | |||||
| @Override | |||||
| public boolean start() { | |||||
| String[] args = getArgs(); | |||||
| if (args.length != 3) { | |||||
| LOGGER.log(Level.SEVERE, | |||||
| "usage: GenerateTimeZonesMapData /path/to/input/text_file " + | |||||
| "/path/to/output/directory"); | |||||
| return false; | |||||
| } | |||||
| try { | |||||
| GenerateTimeZonesMapData generateTimeZonesMapData = new GenerateTimeZonesMapData( | |||||
| new File(args[1]), new PhonePrefixDataIOHandler(new File(args[2]))); | |||||
| generateTimeZonesMapData.run(); | |||||
| } catch (IOException e) { | |||||
| LOGGER.log(Level.SEVERE, e.getMessage()); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,136 @@ | |||||
| /* | |||||
| * Copyright (C) 2012 The Libphonenumber Authors | |||||
| * | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.google.i18n.phonenumbers.buildtools; | |||||
| import com.google.i18n.phonenumbers.prefixmapper.PrefixTimeZonesMap; | |||||
| import junit.framework.TestCase; | |||||
| import java.io.ByteArrayInputStream; | |||||
| import java.io.ByteArrayOutputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.ObjectInputStream; | |||||
| import java.util.Map; | |||||
| import java.util.SortedMap; | |||||
| /** | |||||
| * Unittests for GenerateTimeZonesMapData.java | |||||
| * | |||||
| * @author Walter Erquinigo | |||||
| */ | |||||
| public class GenerateTimeZonesMapDataTest extends TestCase { | |||||
| private static final String BRUSSELS_TZ = "Europe/Brussels"; | |||||
| private static final String PARIS_TZ = "Europe/Paris"; | |||||
| private static final String PARIS_BRUSSELS_LINES = | |||||
| "322|" + BRUSSELS_TZ + "\n331|" + PARIS_TZ + "\n"; | |||||
| private static SortedMap<Integer, String> parseTextFileHelper(String input) throws IOException { | |||||
| return GenerateTimeZonesMapData.parseTextFile(new ByteArrayInputStream(input.getBytes())); | |||||
| } | |||||
| public void testParseTextFile() throws IOException { | |||||
| Map<Integer, String> result = parseTextFileHelper(PARIS_BRUSSELS_LINES); | |||||
| assertEquals(2, result.size()); | |||||
| assertEquals(PARIS_TZ, result.get(331)); | |||||
| assertEquals(BRUSSELS_TZ, result.get(322)); | |||||
| } | |||||
| public void testParseTextFileIgnoresComments() throws IOException { | |||||
| Map<Integer, String> result = parseTextFileHelper("# Hello\n" + PARIS_BRUSSELS_LINES); | |||||
| assertEquals(2, result.size()); | |||||
| assertEquals(PARIS_TZ, result.get(331)); | |||||
| assertEquals(BRUSSELS_TZ, result.get(322)); | |||||
| } | |||||
| public void testParseTextFileIgnoresBlankLines() throws IOException { | |||||
| Map<Integer, String> result = parseTextFileHelper("\n" + PARIS_BRUSSELS_LINES); | |||||
| assertEquals(2, result.size()); | |||||
| assertEquals(PARIS_TZ, result.get(331)); | |||||
| assertEquals(BRUSSELS_TZ, result.get(322)); | |||||
| } | |||||
| public void testParseTextFileIgnoresTrailingWhitespaces() throws IOException { | |||||
| Map<Integer, String> result = parseTextFileHelper( | |||||
| "331|" + PARIS_TZ + "\n322|" + BRUSSELS_TZ + " \n"); | |||||
| assertEquals(2, result.size()); | |||||
| assertEquals(PARIS_TZ, result.get(331)); | |||||
| assertEquals(BRUSSELS_TZ, result.get(322)); | |||||
| } | |||||
| public void testParseTextFileThrowsExceptionWithMalformattedData() throws IOException { | |||||
| try { | |||||
| parseTextFileHelper("331"); | |||||
| fail(); | |||||
| } catch (RuntimeException e) { | |||||
| // Expected. | |||||
| } | |||||
| } | |||||
| public void testParseTextFileThrowsExceptionWithMissingTimeZone() throws IOException { | |||||
| try { | |||||
| parseTextFileHelper("331|"); | |||||
| fail(); | |||||
| } catch (RuntimeException e) { | |||||
| // Expected. | |||||
| } | |||||
| } | |||||
| // Returns a String representing the input after serialization and deserialization by | |||||
| // PrefixTimeZonesMap. | |||||
| private static String convertDataHelper(String input) throws IOException { | |||||
| ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(input.getBytes()); | |||||
| ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | |||||
| SortedMap<Integer, String> prefixTimeZonesMapping = parseTextFileHelper(input); | |||||
| GenerateTimeZonesMapData.writeToBinaryFile(prefixTimeZonesMapping, byteArrayOutputStream); | |||||
| // The byte array output stream now contains the corresponding serialized prefix to time zones | |||||
| // map. Try to deserialize it and compare it with the initial input. | |||||
| PrefixTimeZonesMap prefixTimeZonesMap = new PrefixTimeZonesMap(); | |||||
| prefixTimeZonesMap.readExternal( | |||||
| new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()))); | |||||
| return prefixTimeZonesMap.toString(); | |||||
| } | |||||
| public void testConvertData() throws IOException { | |||||
| String input = PARIS_BRUSSELS_LINES; | |||||
| String dataAfterDeserialization = convertDataHelper(input); | |||||
| assertEquals(input, dataAfterDeserialization); | |||||
| } | |||||
| public void testConvertThrowsExceptionWithMissingTimeZone() throws IOException { | |||||
| String input = PARIS_BRUSSELS_LINES + "3341|\n"; | |||||
| try { | |||||
| String dataAfterDeserialization = convertDataHelper(input); | |||||
| } catch (RuntimeException e) { | |||||
| // Expected. | |||||
| } | |||||
| } | |||||
| public void testConvertDataThrowsExceptionWithDuplicatedPrefixes() throws IOException { | |||||
| String input = "331|" + PARIS_TZ + "\n331|" + BRUSSELS_TZ + "\n"; | |||||
| try { | |||||
| convertDataHelper(input); | |||||
| fail(); | |||||
| } catch (RuntimeException e) { | |||||
| // Expected. | |||||
| } | |||||
| } | |||||
| } | |||||