Browse Source

JAVA: Added the phone number to timezones mapper, including tests, binary generation code and data

pull/567/head
Cecilia Roes 12 years ago
committed by Mihaela Rosca
parent
commit
23f12e8b5f
19 changed files with 3065 additions and 7 deletions
  1. +26
    -3
      java/build.xml
  2. +11
    -0
      java/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java
  3. +8
    -0
      java/geocoder/pom.xml
  4. +194
    -0
      java/geocoder/src/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapper.java
  5. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/timezones/data/map_data
  6. +136
    -0
      java/geocoder/test/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapperTest.java
  7. BIN
      java/geocoder/test/com/google/i18n/phonenumbers/timezones/testing_data/map_data
  8. +15
    -4
      java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PhonePrefixMap.java
  9. +128
    -0
      java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMap.java
  10. +178
    -0
      java/internal/prefixmapper/test/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMapTest.java
  11. +8
    -0
      java/release_notes.txt
  12. +24
    -0
      resources/test/timezones/map_data.txt
  13. +1996
    -0
      resources/timezones/map_data.txt
  14. BIN
      tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar
  15. +2
    -0
      tools/java/java-build/src/com/google/i18n/phonenumbers/EntryPoint.java
  16. +143
    -0
      tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapData.java
  17. +60
    -0
      tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataEntryPoint.java
  18. BIN
      tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar
  19. +136
    -0
      tools/java/java-build/test/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataTest.java

+ 26
- 3
java/build.xml View File

@ -127,6 +127,26 @@
</exec>
</target>
<target name="build-timezones-data">
<exec executable="java">
<arg value="-jar" />
<arg value="${build.tools.jar}"/>
<arg value="GenerateTimeZonesMapData"/>
<arg value="${resources.dir}/timezones/map_data.txt"/>
<arg value="geocoder/src/com/google/i18n/phonenumbers/timezones/data"/>
</exec>
</target>
<target name="build-timezones-test-data">
<exec executable="java">
<arg value="-jar" />
<arg value="${build.tools.jar}"/>
<arg value="GenerateTimeZonesMapData"/>
<arg value="${resources.dir}/test/timezones/map_data.txt"/>
<arg value="geocoder/test/com/google/i18n/phonenumbers/timezones/testing_data"/>
</exec>
</target>
<target name="build-js-metadata">
<exec executable="java">
<arg value="-jar" />
@ -153,7 +173,7 @@
</target>
<target name="compile" description="Compile Java source."
depends="build-phone-metadata,build-short-metadata,build-alternate-metadata,build-carrier-data,build-geo-data">
depends="build-phone-metadata,build-short-metadata,build-alternate-metadata,build-carrier-data,build-geo-data,build-timezones-data">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${libphonenumber.src.dir};${carrier.src.dir};${geocoder.src.dir};${prefixmapper.src.dir}"
destdir="${classes.dir}" classpathref="classpath" includeAntRuntime="false"/>
@ -169,7 +189,7 @@
<include name="**/*.class"/>
<exclude name="**/*Test*"/>
<exclude name="**/BuildMetadata*"/>
<exclude name="**/PhoneNumberToCarrierMapper*"/>
<exclude name="**/PhoneNumberTo*"/>
<exclude name="**/geocoding/*"/>
<exclude name="**/prefixmapper/*"/>
</fileset>
@ -192,17 +212,19 @@
<jar destfile="${jar.dir}/offline-geocoder.jar">
<fileset dir="${classes.dir}">
<include name="**/geocoding/*.class"/>
<include name="**/PhoneNumberToTimeZonesMapper*"/>
<include name="**/prefixmapper/*.class"/>
<exclude name="**/*Test*"/>
</fileset>
<fileset dir="${geocoder.src.dir}">
<include name="**/geocoding/data/*"/>
<include name="**/timezones/data/*"/>
</fileset>
</jar>
</target>
<target name="test-jar"
depends="compile,build-test-metadata,build-carrier-test-data,build-geo-test-data">
depends="compile,build-test-metadata,build-carrier-test-data,build-geo-test-data,build-timezones-test-data">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}-test.jar">
<fileset dir="${classes.dir}">
@ -222,6 +244,7 @@
</fileset>
<fileset dir="${geocoder.test.dir}">
<include name="**/geocoding/testing_data/*"/>
<include name="**/timezones/testing_data/*"/>
</fileset>
</jar>
</target>


+ 11
- 0
java/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java View File

@ -21,6 +21,7 @@ package com.google.phonenumbers;
import com.google.i18n.phonenumbers.AsYouTypeFormatter;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberToCarrierMapper;
import com.google.i18n.phonenumbers.PhoneNumberToTimeZonesMapper;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType;
@ -264,6 +265,16 @@ public class PhoneNumberParserServlet extends HttpServlet {
output.append("</TABLE>");
output.append("</DIV>");
output.append("<DIV>");
output.append("<TABLE border=1>");
output.append("<TR><TD colspan=2>PhoneNumberToTimeZonesMapper Results</TD></TR>");
appendLine(
"Time zone(s)",
PhoneNumberToTimeZonesMapper.getInstance().getTimeZonesForNumber(number).toString(),
output);
output.append("</TABLE>");
output.append("</DIV>");
if (numberType == PhoneNumberType.MOBILE ||
numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE ||
numberType == PhoneNumberType.PAGER) {


+ 8
- 0
java/geocoder/pom.xml View File

@ -21,12 +21,20 @@
<directory>src/com/google/i18n/phonenumbers/geocoding/data</directory>
<targetPath>com/google/i18n/phonenumbers/geocoding/data</targetPath>
</resource>
<resource>
<directory>src/com/google/i18n/phonenumbers/timezones/data</directory>
<targetPath>com/google/i18n/phonenumbers/timezones/data</targetPath>
</resource>
</resources>
<testResources>
<testResource>
<directory>test/com/google/i18n/phonenumbers/geocoding/testing_data</directory>
<targetPath>com/google/i18n/phonenumbers/geocoding/testing_data</targetPath>
</testResource>
<testResource>
<directory>test/com/google/i18n/phonenumbers/timezones/testing_data</directory>
<targetPath>com/google/i18n/phonenumbers/timezones/testing_data</targetPath>
</testResource>
</testResources>
</build>


+ 194
- 0
java/geocoder/src/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapper.java View File

@ -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);
}
}

BIN
java/geocoder/src/com/google/i18n/phonenumbers/timezones/data/map_data View File


+ 136
- 0
java/geocoder/test/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapperTest.java View File

@ -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());
}
}

BIN
java/geocoder/test/com/google/i18n/phonenumbers/timezones/testing_data/map_data View File


+ 15
- 4
java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PhonePrefixMap.java View File

@ -144,13 +144,12 @@ public class PhonePrefixMap implements Externalizable {
* @param number the phone number to look up
* @return the description of the number
*/
String lookup(PhoneNumber number) {
int numOfEntries = phonePrefixMapStorage.getNumOfEntries();
String lookup(long number) {
int numOfEntries = phonePrefixMapStorage.getNumOfEntries();
if (numOfEntries == 0) {
return null;
}
long phonePrefix =
Long.parseLong(number.getCountryCode() + phoneUtil.getNationalSignificantNumber(number));
long phonePrefix = number;
int currentIndex = numOfEntries - 1;
SortedSet<Integer> currentSetOfLengths = phonePrefixMapStorage.getPossibleLengths();
while (currentSetOfLengths.size() > 0) {
@ -172,6 +171,18 @@ public class PhonePrefixMap implements Externalizable {
return null;
}
/**
* As per {@link #lookup(long)}, but receives the number as a PhoneNumber instead of a long.
*
* @param number the phone number to look up
* @return the description corresponding to the prefix that best matches this phone number
*/
public String lookup(PhoneNumber number) {
long phonePrefix =
Long.parseLong(number.getCountryCode() + phoneUtil.getNationalSignificantNumber(number));
return lookup(phonePrefix);
}
/**
* Does a binary search for {@code value} in the provided array from {@code start} to {@code end}
* (inclusive). Returns the position if {@code value} is found; otherwise, returns the


+ 128
- 0
java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMap.java View File

@ -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();
}
}

+ 178
- 0
java/internal/prefixmapper/test/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMapTest.java View File

@ -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());
}
}

+ 8
- 0
java/release_notes.txt View File

@ -1,3 +1,11 @@
Oct 2, 2013:
* Code changes:
- Added PhoneNumberToTimeZonesMapper including unittests to the geocoder maven project.
- Added build rules for generating the binary time zones mapping file from the text file.
- Modified PhoneNumberParserServlet.java (the appengine demo) to incorporate time zone mapping.
* Metadata changes:
- Added the time zone mapping file.
Sep 30, 2013:
* Code changes:
- Added PhoneNumberToCarrierMapper including unittests.


+ 24
- 0
resources/test/timezones/map_data.txt View File

@ -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

+ 1996
- 0
resources/timezones/map_data.txt
File diff suppressed because it is too large
View File


BIN
tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar View File


+ 2
- 0
tools/java/java-build/src/com/google/i18n/phonenumbers/EntryPoint.java View File

@ -17,6 +17,7 @@
package com.google.i18n.phonenumbers;
import com.google.i18n.phonenumbers.buildtools.GeneratePhonePrefixDataEntryPoint;
import com.google.i18n.phonenumbers.buildtools.GenerateTimeZonesMapDataEntryPoint;
/**
* Entry point class for Java and JavaScript build tools.
@ -30,6 +31,7 @@ public class EntryPoint {
new BuildMetadataJsonFromXml(),
new BuildMetadataProtoFromXml(),
new GeneratePhonePrefixDataEntryPoint(),
new GenerateTimeZonesMapDataEntryPoint(),
}).start();
System.exit(status ? 0 : 1);


+ 143
- 0
tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapData.java View File

@ -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.");
}
}

+ 60
- 0
tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataEntryPoint.java View File

@ -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;
}
}

BIN
tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar View File


+ 136
- 0
tools/java/java-build/test/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataTest.java View File

@ -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.
}
}
}

Loading…
Cancel
Save