| @ -0,0 +1,56 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.internal; | |||||
| import com.google.i18n.phonenumbers.CountryCodeToRegionCodeMap; | |||||
| import java.util.List; | |||||
| /** | |||||
| * Utility class for checking whether identifiers region code and country calling code belong | |||||
| * to geographical entities. For more information about geo vs. non-geo entities see {@link | |||||
| * com.google.i18n.phonenumbers.metadata.source.RegionMetadataSource} and {@link | |||||
| * com.google.i18n.phonenumbers.metadata.source.NonGeographicalEntityMetadataSource} | |||||
| */ | |||||
| public final class GeoEntityUtility { | |||||
| /** Region code with a special meaning, used to mark non-geographical entities */ | |||||
| public static final String REGION_CODE_FOR_NON_GEO_ENTITIES = "001"; | |||||
| /** Determines whether {@code regionCode} belongs to a geographical entity. */ | |||||
| public static boolean isGeoEntity(String regionCode) { | |||||
| return !regionCode.equals(REGION_CODE_FOR_NON_GEO_ENTITIES); | |||||
| } | |||||
| /** | |||||
| * Determines whether {@code countryCallingCode} belongs to a geographical entity. | |||||
| * | |||||
| * <p>A single country calling code could map to several different regions. It is considered that | |||||
| * {@code countryCallingCode} belongs to a geo entity if all of these regions are geo entities | |||||
| * | |||||
| * <p>Note that this method will not throw an exception even when the underlying mapping for the | |||||
| * {@code countryCallingCode} does not exist, instead it will return {@code false} | |||||
| */ | |||||
| public static boolean isGeoEntity(int countryCallingCode) { | |||||
| List<String> regionCodesForCountryCallingCode = | |||||
| CountryCodeToRegionCodeMap.getCountryCodeToRegionCodeMap().get(countryCallingCode); | |||||
| return regionCodesForCountryCallingCode != null | |||||
| && !regionCodesForCountryCallingCode.contains(REGION_CODE_FOR_NON_GEO_ENTITIES); | |||||
| } | |||||
| private GeoEntityUtility() {} | |||||
| } | |||||
| @ -0,0 +1,92 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata; | |||||
| import com.google.i18n.phonenumbers.MetadataLoader; | |||||
| import com.google.i18n.phonenumbers.metadata.init.ClassPathResourceMetadataLoader; | |||||
| import com.google.i18n.phonenumbers.metadata.init.MetadataParser; | |||||
| import com.google.i18n.phonenumbers.metadata.source.FormattingMetadataSource; | |||||
| import com.google.i18n.phonenumbers.metadata.source.FormattingMetadataSourceImpl; | |||||
| import com.google.i18n.phonenumbers.metadata.source.MetadataSource; | |||||
| import com.google.i18n.phonenumbers.metadata.source.MetadataSourceImpl; | |||||
| import com.google.i18n.phonenumbers.metadata.source.RegionMetadataSource; | |||||
| import com.google.i18n.phonenumbers.metadata.source.RegionMetadataSourceImpl; | |||||
| import com.google.i18n.phonenumbers.metadata.source.SingleFileModeFileNameProvider; | |||||
| /** | |||||
| * Provides metadata init & source dependencies when metadata is stored in multi-file mode and | |||||
| * loaded as a classpath resource. | |||||
| */ | |||||
| public final class DefaultMetadataDependenciesProvider { | |||||
| private static final DefaultMetadataDependenciesProvider INSTANCE = new DefaultMetadataDependenciesProvider(); | |||||
| public static DefaultMetadataDependenciesProvider getInstance() { | |||||
| return INSTANCE; | |||||
| } | |||||
| private DefaultMetadataDependenciesProvider() {} | |||||
| private final MetadataParser metadataParser = MetadataParser.newStrictParser(); | |||||
| private final MetadataLoader metadataLoader = new ClassPathResourceMetadataLoader(); | |||||
| private final MetadataSource phoneNumberMetadataSource = | |||||
| new MetadataSourceImpl( | |||||
| new SingleFileModeFileNameProvider( | |||||
| "/com/google/i18n/phonenumbers/buildtools/PhoneNumberMetadataProto"), | |||||
| metadataLoader, | |||||
| metadataParser); | |||||
| private final RegionMetadataSource shortNumberMetadataSource = | |||||
| new RegionMetadataSourceImpl( | |||||
| new SingleFileModeFileNameProvider( | |||||
| "/com/google/i18n/phonenumbers/buildtools/ShortNumberMetadataProto"), | |||||
| metadataLoader, | |||||
| metadataParser); | |||||
| private final FormattingMetadataSource alternateFormatsMetadataSource = | |||||
| new FormattingMetadataSourceImpl( | |||||
| new SingleFileModeFileNameProvider( | |||||
| "/com/google/i18n/phonenumbers/buildtools/PhoneNumberAlternateFormatsProto"), | |||||
| metadataLoader, | |||||
| metadataParser); | |||||
| public MetadataParser getMetadataParser() { | |||||
| return metadataParser; | |||||
| } | |||||
| public MetadataLoader getMetadataLoader() { | |||||
| return metadataLoader; | |||||
| } | |||||
| public MetadataSource getPhoneNumberMetadataSource() { | |||||
| return phoneNumberMetadataSource; | |||||
| } | |||||
| public RegionMetadataSource getShortNumberMetadataSource() { | |||||
| return shortNumberMetadataSource; | |||||
| } | |||||
| public FormattingMetadataSource getAlternateFormatsMetadataSource() { | |||||
| return alternateFormatsMetadataSource; | |||||
| } | |||||
| public String getCarrierDataDirectory() { | |||||
| return "/com/google/i18n/phonenumbers/buildtools/carrier_data/"; | |||||
| } | |||||
| public String getGeocodingDataDirectory() { | |||||
| return "/com/google/i18n/phonenumbers/buildtools/geocoding_data/"; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,32 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.init; | |||||
| import com.google.i18n.phonenumbers.MetadataLoader; | |||||
| import java.io.InputStream; | |||||
| /** | |||||
| * A {@link MetadataLoader} implementation that reads phone number metadata files as classpath | |||||
| * resources. | |||||
| */ | |||||
| public final class ClassPathResourceMetadataLoader implements MetadataLoader { | |||||
| @Override | |||||
| public InputStream loadMetadata(String metadataFileName) { | |||||
| return ClassPathResourceMetadataLoader.class.getResourceAsStream(metadataFileName); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,108 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.init; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.io.ObjectInputStream; | |||||
| import java.util.Collection; | |||||
| import java.util.Collections; | |||||
| import java.util.List; | |||||
| import java.util.logging.Level; | |||||
| import java.util.logging.Logger; | |||||
| /** | |||||
| * Exposes single method for parsing {@link InputStream} content into {@link Collection} of {@link | |||||
| * PhoneMetadata} | |||||
| */ | |||||
| public final class MetadataParser { | |||||
| private static final Logger logger = Logger.getLogger(MetadataParser.class.getName()); | |||||
| /** | |||||
| * Creates new instance in lenient mode, see {@link MetadataParser#parse(InputStream)} for more | |||||
| * info. | |||||
| */ | |||||
| public static MetadataParser newLenientParser() { | |||||
| return new MetadataParser(false); | |||||
| } | |||||
| /** | |||||
| * Creates new instance in strict mode, see {@link MetadataParser#parse(InputStream)} for more | |||||
| * info | |||||
| */ | |||||
| public static MetadataParser newStrictParser() { | |||||
| return new MetadataParser(true); | |||||
| } | |||||
| private final boolean strictMode; | |||||
| private MetadataParser(boolean strictMode) { | |||||
| this.strictMode = strictMode; | |||||
| } | |||||
| /** | |||||
| * Parses given {@link InputStream} into a {@link Collection} of {@link PhoneMetadata}. | |||||
| * | |||||
| * @throws IllegalArgumentException if {@code source} is {@code null} and strict mode is on | |||||
| * @return parsed {@link PhoneMetadata}, or empty {@link Collection} if {@code source} is {@code | |||||
| * null} and lenient mode is on | |||||
| */ | |||||
| public Collection<PhoneMetadata> parse(InputStream source) { | |||||
| if (source == null) { | |||||
| return handleNullSource(); | |||||
| } | |||||
| ObjectInputStream ois = null; | |||||
| try { | |||||
| ois = new ObjectInputStream(source); | |||||
| PhoneMetadataCollection phoneMetadataCollection = new PhoneMetadataCollection(); | |||||
| phoneMetadataCollection.readExternal(ois); | |||||
| List<PhoneMetadata> phoneMetadata = phoneMetadataCollection.getMetadataList(); | |||||
| // Sanity check; this should not happen if provided InputStream is valid | |||||
| if (phoneMetadata.isEmpty()) { | |||||
| throw new IllegalStateException("Empty metadata"); | |||||
| } | |||||
| return phoneMetadataCollection.getMetadataList(); | |||||
| } catch (IOException e) { | |||||
| throw new IllegalStateException("Unable to parse metadata file", e); | |||||
| } finally { | |||||
| if (ois != null) { | |||||
| // This will close all underlying streams as well, including source. | |||||
| close(ois); | |||||
| } else { | |||||
| close(source); | |||||
| } | |||||
| } | |||||
| } | |||||
| private List<PhoneMetadata> handleNullSource() { | |||||
| if (strictMode) { | |||||
| throw new IllegalArgumentException("Source cannot be null"); | |||||
| } | |||||
| return Collections.emptyList(); | |||||
| } | |||||
| private void close(InputStream inputStream) { | |||||
| try { | |||||
| inputStream.close(); | |||||
| } catch (IOException e) { | |||||
| logger.log(Level.WARNING, "Error closing input stream (ignored)", e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,78 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.MetadataLoader; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.metadata.init.MetadataParser; | |||||
| import java.io.InputStream; | |||||
| import java.util.Collection; | |||||
| import java.util.HashSet; | |||||
| import java.util.Set; | |||||
| /** | |||||
| * A blocking implementation of {@link MetadataBootstrappingGuard}. Can be used for both single-file | |||||
| * (bulk) and multi-file metadata | |||||
| * | |||||
| * @param <T> needs to extend {@link MetadataContainer} | |||||
| */ | |||||
| final class BlockingMetadataBootstrappingGuard<T extends MetadataContainer> | |||||
| implements MetadataBootstrappingGuard<T> { | |||||
| private final MetadataLoader metadataLoader; | |||||
| private final MetadataParser metadataParser; | |||||
| private final T metadataContainer; | |||||
| private final Set<String> loadedFiles; | |||||
| BlockingMetadataBootstrappingGuard( | |||||
| MetadataLoader metadataLoader, MetadataParser metadataParser, T metadataContainer) { | |||||
| this.metadataLoader = metadataLoader; | |||||
| this.metadataParser = metadataParser; | |||||
| this.metadataContainer = metadataContainer; | |||||
| this.loadedFiles = new HashSet<>(); | |||||
| } | |||||
| @Override | |||||
| public T getOrBootstrap(String phoneMetadataFile) { | |||||
| if (!loadedFiles.contains(phoneMetadataFile)) { | |||||
| bootstrapMetadata(phoneMetadataFile); | |||||
| } | |||||
| return metadataContainer; | |||||
| } | |||||
| private synchronized void bootstrapMetadata(String phoneMetadataFile) { | |||||
| // Additional check is needed because multiple threads could pass the first check when calling | |||||
| // getOrBootstrap() at the same time for unloaded metadata file | |||||
| if (loadedFiles.contains(phoneMetadataFile)) { | |||||
| return; | |||||
| } | |||||
| Collection<PhoneMetadata> phoneMetadata = read(phoneMetadataFile); | |||||
| for (PhoneMetadata metadata : phoneMetadata) { | |||||
| metadataContainer.accept(metadata); | |||||
| } | |||||
| loadedFiles.add(phoneMetadataFile); | |||||
| } | |||||
| private Collection<PhoneMetadata> read(String phoneMetadataFile) { | |||||
| try { | |||||
| InputStream metadataStream = metadataLoader.loadMetadata(phoneMetadataFile); | |||||
| return metadataParser.parse(metadataStream); | |||||
| } catch (IllegalArgumentException | IllegalStateException e) { | |||||
| throw new IllegalStateException("Failed to read file " + phoneMetadataFile, e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,69 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.internal.GeoEntityUtility; | |||||
| /** | |||||
| * Implementation of {@link MetadataContainer} which is a composition of different {@link | |||||
| * MapBackedMetadataContainer}s. It adds items to a single simpler container at a time depending on | |||||
| * the content of {@link PhoneMetadata}. | |||||
| */ | |||||
| final class CompositeMetadataContainer implements MetadataContainer { | |||||
| private final MapBackedMetadataContainer<Integer> metadataByCountryCode = | |||||
| MapBackedMetadataContainer.byCountryCallingCode(); | |||||
| private final MapBackedMetadataContainer<String> metadataByRegionCode = | |||||
| MapBackedMetadataContainer.byRegionCode(); | |||||
| /** | |||||
| * Intended to be called for geographical regions only. For non-geographical entities, use {@link | |||||
| * CompositeMetadataContainer#getMetadataBy(int)} | |||||
| */ | |||||
| PhoneMetadata getMetadataBy(String regionCode) { | |||||
| return metadataByRegionCode.getMetadataBy(regionCode); | |||||
| } | |||||
| /** | |||||
| * Intended to be called for non-geographical entities only, such as 800 (country code assigned to | |||||
| * the Universal International Freephone Service). For geographical regions, use {@link | |||||
| * CompositeMetadataContainer#getMetadataBy(String)} | |||||
| */ | |||||
| PhoneMetadata getMetadataBy(int countryCallingCode) { | |||||
| return metadataByCountryCode.getMetadataBy(countryCallingCode); | |||||
| } | |||||
| /** | |||||
| * If the metadata belongs to a specific geographical region (it has a region code other than | |||||
| * {@link GeoEntityUtility#REGION_CODE_FOR_NON_GEO_ENTITIES}), it will be added to a {@link | |||||
| * MapBackedMetadataContainer} which stores metadata by region code. Otherwise, it will be added | |||||
| * to a {@link MapBackedMetadataContainer} which stores metadata by country calling code. This | |||||
| * means that {@link CompositeMetadataContainer#getMetadataBy(int)} will not work for country | |||||
| * calling codes such as 41 (country calling code for Switzerland), only for country calling codes | |||||
| * such as 800 (country code assigned to the Universal International Freephone Service) | |||||
| */ | |||||
| @Override | |||||
| public void accept(PhoneMetadata phoneMetadata) { | |||||
| String regionCode = metadataByRegionCode.getKeyProvider().getKeyOf(phoneMetadata); | |||||
| if (GeoEntityUtility.isGeoEntity(regionCode)) { | |||||
| metadataByRegionCode.accept(phoneMetadata); | |||||
| } else { | |||||
| metadataByCountryCode.accept(phoneMetadata); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,37 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| /** A source of formatting phone metadata. */ | |||||
| public interface FormattingMetadataSource { | |||||
| /** | |||||
| * Returns formatting phone metadata for provided country calling code. | |||||
| * | |||||
| * <p>This method is similar to the one in {@link | |||||
| * NonGeographicalEntityMetadataSource#getMetadataForNonGeographicalRegion(int)}, except that it | |||||
| * will not fail for geographical regions, it can be used for both geo- and non-geo entities. | |||||
| * | |||||
| * <p>In case the provided {@code countryCallingCode} maps to several different regions, only one | |||||
| * would contain formatting metadata. | |||||
| * | |||||
| * @return the phone metadata for provided {@code countryCallingCode}, or null if there is none. | |||||
| */ | |||||
| PhoneMetadata getFormattingMetadataForCountryCallingCode(int countryCallingCode); | |||||
| } | |||||
| @ -0,0 +1,57 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.MetadataLoader; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.metadata.init.MetadataParser; | |||||
| /** | |||||
| * Implementation of {@link FormattingMetadataSource} guarded by {@link MetadataBootstrappingGuard} | |||||
| * | |||||
| * <p>By default, a {@link BlockingMetadataBootstrappingGuard} will be used, but any custom | |||||
| * implementation can be injected. | |||||
| */ | |||||
| public final class FormattingMetadataSourceImpl implements FormattingMetadataSource { | |||||
| private final PhoneMetadataFileNameProvider phoneMetadataFileNameProvider; | |||||
| private final MetadataBootstrappingGuard<MapBackedMetadataContainer<Integer>> bootstrappingGuard; | |||||
| public FormattingMetadataSourceImpl( | |||||
| PhoneMetadataFileNameProvider phoneMetadataFileNameProvider, | |||||
| MetadataBootstrappingGuard<MapBackedMetadataContainer<Integer>> bootstrappingGuard) { | |||||
| this.phoneMetadataFileNameProvider = phoneMetadataFileNameProvider; | |||||
| this.bootstrappingGuard = bootstrappingGuard; | |||||
| } | |||||
| public FormattingMetadataSourceImpl( | |||||
| PhoneMetadataFileNameProvider phoneMetadataFileNameProvider, | |||||
| MetadataLoader metadataLoader, | |||||
| MetadataParser metadataParser) { | |||||
| this( | |||||
| phoneMetadataFileNameProvider, | |||||
| new BlockingMetadataBootstrappingGuard<>( | |||||
| metadataLoader, metadataParser, MapBackedMetadataContainer.byCountryCallingCode())); | |||||
| } | |||||
| @Override | |||||
| public PhoneMetadata getFormattingMetadataForCountryCallingCode(int countryCallingCode) { | |||||
| return bootstrappingGuard | |||||
| .getOrBootstrap(phoneMetadataFileNameProvider.getFor(countryCallingCode)) | |||||
| .getMetadataBy(countryCallingCode); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,74 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import java.util.concurrent.ConcurrentHashMap; | |||||
| import java.util.concurrent.ConcurrentMap; | |||||
| /** | |||||
| * A {@link MetadataContainer} implementation backed by a {@link ConcurrentHashMap} with generic | |||||
| * keys. | |||||
| */ | |||||
| final class MapBackedMetadataContainer<T> implements MetadataContainer { | |||||
| static MapBackedMetadataContainer<String> byRegionCode() { | |||||
| return new MapBackedMetadataContainer<>( | |||||
| new KeyProvider<String>() { | |||||
| @Override | |||||
| public String getKeyOf(PhoneMetadata phoneMetadata) { | |||||
| return phoneMetadata.getId(); | |||||
| } | |||||
| }); | |||||
| } | |||||
| static MapBackedMetadataContainer<Integer> byCountryCallingCode() { | |||||
| return new MapBackedMetadataContainer<>( | |||||
| new KeyProvider<Integer>() { | |||||
| @Override | |||||
| public Integer getKeyOf(PhoneMetadata phoneMetadata) { | |||||
| return phoneMetadata.getCountryCode(); | |||||
| } | |||||
| }); | |||||
| } | |||||
| private final ConcurrentMap<T, PhoneMetadata> metadataMap; | |||||
| private final KeyProvider<T> keyProvider; | |||||
| private MapBackedMetadataContainer(KeyProvider<T> keyProvider) { | |||||
| this.metadataMap = new ConcurrentHashMap<>(); | |||||
| this.keyProvider = keyProvider; | |||||
| } | |||||
| PhoneMetadata getMetadataBy(T key) { | |||||
| return key != null ? metadataMap.get(key) : null; | |||||
| } | |||||
| KeyProvider<T> getKeyProvider() { | |||||
| return keyProvider; | |||||
| } | |||||
| @Override | |||||
| public void accept(PhoneMetadata phoneMetadata) { | |||||
| metadataMap.put(keyProvider.getKeyOf(phoneMetadata), phoneMetadata); | |||||
| } | |||||
| interface KeyProvider<T> { | |||||
| T getKeyOf(PhoneMetadata phoneMetadata); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,35 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| /** | |||||
| * Guard that ensures that metadata bootstrapping process (loading and parsing) is triggered only | |||||
| * once per metadata file. | |||||
| * | |||||
| * @param <T> needs to extend {@link MetadataContainer} | |||||
| */ | |||||
| public interface MetadataBootstrappingGuard<T extends MetadataContainer> { | |||||
| /** | |||||
| * If metadata from the provided file has not yet been read, invokes loading and parsing from the | |||||
| * provided file and adds the result to guarded {@link MetadataContainer}. | |||||
| * | |||||
| * @param phoneMetadataFile to read from | |||||
| * @return guarded {@link MetadataContainer} | |||||
| */ | |||||
| T getOrBootstrap(String phoneMetadataFile); | |||||
| } | |||||
| @ -0,0 +1,32 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| /** | |||||
| * A container for {@link PhoneMetadata} | |||||
| */ | |||||
| interface MetadataContainer { | |||||
| /** | |||||
| * Adds {@link PhoneMetadata} to the container. It depends on the implementation of the interface | |||||
| * what this means, for example {@link MapBackedMetadataContainer} simply adds the provided | |||||
| * metadata into the backing map. Implementing classes should ensure thread-safety. | |||||
| */ | |||||
| void accept(PhoneMetadata phoneMetadata); | |||||
| } | |||||
| @ -0,0 +1,21 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| /** A source of phone metadata split by different regions. */ | |||||
| public interface MetadataSource extends RegionMetadataSource, NonGeographicalEntityMetadataSource { | |||||
| } | |||||
| @ -0,0 +1,72 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.MetadataLoader; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.internal.GeoEntityUtility; | |||||
| import com.google.i18n.phonenumbers.metadata.init.MetadataParser; | |||||
| /** | |||||
| * Implementation of {@link MetadataSource} guarded by {@link MetadataBootstrappingGuard}. | |||||
| * | |||||
| * <p>By default, a {@link BlockingMetadataBootstrappingGuard} will be used, but any custom | |||||
| * implementation can be injected. | |||||
| */ | |||||
| public final class MetadataSourceImpl implements MetadataSource { | |||||
| private final PhoneMetadataFileNameProvider phoneMetadataFileNameProvider; | |||||
| private final MetadataBootstrappingGuard<CompositeMetadataContainer> bootstrappingGuard; | |||||
| public MetadataSourceImpl( | |||||
| PhoneMetadataFileNameProvider phoneMetadataFileNameProvider, | |||||
| MetadataBootstrappingGuard<CompositeMetadataContainer> bootstrappingGuard) { | |||||
| this.phoneMetadataFileNameProvider = phoneMetadataFileNameProvider; | |||||
| this.bootstrappingGuard = bootstrappingGuard; | |||||
| } | |||||
| public MetadataSourceImpl( | |||||
| PhoneMetadataFileNameProvider phoneMetadataFileNameProvider, | |||||
| MetadataLoader metadataLoader, | |||||
| MetadataParser metadataParser) { | |||||
| this( | |||||
| phoneMetadataFileNameProvider, | |||||
| new BlockingMetadataBootstrappingGuard<>( | |||||
| metadataLoader, metadataParser, new CompositeMetadataContainer())); | |||||
| } | |||||
| @Override | |||||
| public PhoneMetadata getMetadataForNonGeographicalRegion(int countryCallingCode) { | |||||
| if (GeoEntityUtility.isGeoEntity(countryCallingCode)) { | |||||
| throw new IllegalArgumentException( | |||||
| countryCallingCode + " calling code belongs to a geo entity"); | |||||
| } | |||||
| return bootstrappingGuard | |||||
| .getOrBootstrap(phoneMetadataFileNameProvider.getFor(countryCallingCode)) | |||||
| .getMetadataBy(countryCallingCode); | |||||
| } | |||||
| @Override | |||||
| public PhoneMetadata getMetadataForRegion(String regionCode) { | |||||
| if (!GeoEntityUtility.isGeoEntity(regionCode)) { | |||||
| throw new IllegalArgumentException(regionCode + " region code is a non-geo entity"); | |||||
| } | |||||
| return bootstrappingGuard | |||||
| .getOrBootstrap(phoneMetadataFileNameProvider.getFor(regionCode)) | |||||
| .getMetadataBy(regionCode); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,42 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import java.util.regex.Pattern; | |||||
| /** | |||||
| * {@link PhoneMetadataFileNameProvider} implementation which appends key as a suffix to the | |||||
| * predefined metadata file name base. | |||||
| */ | |||||
| public final class MultiFileModeFileNameProvider implements PhoneMetadataFileNameProvider { | |||||
| private final String phoneMetadataFileNamePrefix; | |||||
| private static final Pattern ALPHANUMERIC = Pattern.compile("^[\\p{L}\\p{N}]+$"); | |||||
| public MultiFileModeFileNameProvider(String phoneMetadataFileNameBase) { | |||||
| this.phoneMetadataFileNamePrefix = phoneMetadataFileNameBase + "_"; | |||||
| } | |||||
| @Override | |||||
| public String getFor(Object key) { | |||||
| String keyAsString = key.toString(); | |||||
| if (!ALPHANUMERIC.matcher(keyAsString).matches()) { | |||||
| throw new IllegalArgumentException("Invalid key: " + keyAsString); | |||||
| } | |||||
| return phoneMetadataFileNamePrefix + key; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,47 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| /** | |||||
| * A source of phone metadata for non-geographical entities. | |||||
| * | |||||
| * <p>Non-geographical entities are phone number ranges that have a country calling code, but either | |||||
| * do not belong to an actual country (some international services), or belong to a region which has | |||||
| * a different country calling code from the country it is part of. Examples of such ranges are | |||||
| * those starting with: | |||||
| * | |||||
| * <ul> | |||||
| * <li>800 - country code assigned to the Universal International Freephone Service | |||||
| * <li>808 - country code assigned to the International Shared Cost Service | |||||
| * <li>870 - country code assigned to the Pitcairn Islands | |||||
| * <li>... | |||||
| * </ul> | |||||
| */ | |||||
| public interface NonGeographicalEntityMetadataSource { | |||||
| /** | |||||
| * Gets phone metadata for a non-geographical entity. | |||||
| * | |||||
| * @param countryCallingCode the country calling code. | |||||
| * @return the phone metadata for that entity, or null if there is none. | |||||
| * @throws IllegalArgumentException if provided {@code countryCallingCode} does not belong to a | |||||
| * non-geographical entity | |||||
| */ | |||||
| PhoneMetadata getMetadataForNonGeographicalRegion(int countryCallingCode); | |||||
| } | |||||
| @ -0,0 +1,36 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| /** | |||||
| * Abstraction responsible for inferring the metadata file name. | |||||
| * | |||||
| * <p>Two implementations are available: | |||||
| * | |||||
| * <ul> | |||||
| * <li>{@link SingleFileModeFileNameProvider} for single-file metadata. | |||||
| * <li>{@link MultiFileModeFileNameProvider} for multi-file metadata. | |||||
| * </ul> | |||||
| */ | |||||
| public interface PhoneMetadataFileNameProvider { | |||||
| /** | |||||
| * Returns phone metadata file path for the given key. Assumes that key.toString() is | |||||
| * well-defined. | |||||
| */ | |||||
| String getFor(Object key); | |||||
| } | |||||
| @ -0,0 +1,40 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.internal.GeoEntityUtility; | |||||
| /** | |||||
| * A source of phone metadata split by geographical regions. | |||||
| */ | |||||
| public interface RegionMetadataSource { | |||||
| /** | |||||
| * Returns phone metadata for provided geographical region. | |||||
| * | |||||
| * <p>The {@code regionCode} must be different from {@link | |||||
| * GeoEntityUtility#REGION_CODE_FOR_NON_GEO_ENTITIES}, which has a special meaning and is used to | |||||
| * mark non-geographical regions (see {@link NonGeographicalEntityMetadataSource} for more | |||||
| * information). | |||||
| * | |||||
| * @return the phone metadata for provided {@code regionCode}, or null if there is none. | |||||
| * @throws IllegalArgumentException if provided {@code regionCode} is {@link | |||||
| * GeoEntityUtility#REGION_CODE_FOR_NON_GEO_ENTITIES} | |||||
| */ | |||||
| PhoneMetadata getMetadataForRegion(String regionCode); | |||||
| } | |||||
| @ -0,0 +1,62 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import com.google.i18n.phonenumbers.MetadataLoader; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.internal.GeoEntityUtility; | |||||
| import com.google.i18n.phonenumbers.metadata.init.MetadataParser; | |||||
| /** | |||||
| * Implementation of {@link RegionMetadataSource} guarded by {@link MetadataBootstrappingGuard} | |||||
| * | |||||
| * <p>By default, a {@link BlockingMetadataBootstrappingGuard} will be used, but any custom | |||||
| * implementation can be injected. | |||||
| */ | |||||
| public final class RegionMetadataSourceImpl implements RegionMetadataSource { | |||||
| private final PhoneMetadataFileNameProvider phoneMetadataFileNameProvider; | |||||
| private final MetadataBootstrappingGuard<MapBackedMetadataContainer<String>> | |||||
| bootstrappingGuard; | |||||
| public RegionMetadataSourceImpl( | |||||
| PhoneMetadataFileNameProvider phoneMetadataFileNameProvider, | |||||
| MetadataBootstrappingGuard<MapBackedMetadataContainer<String>> bootstrappingGuard) { | |||||
| this.phoneMetadataFileNameProvider = phoneMetadataFileNameProvider; | |||||
| this.bootstrappingGuard = bootstrappingGuard; | |||||
| } | |||||
| public RegionMetadataSourceImpl( | |||||
| PhoneMetadataFileNameProvider phoneMetadataFileNameProvider, | |||||
| MetadataLoader metadataLoader, | |||||
| MetadataParser metadataParser) { | |||||
| this( | |||||
| phoneMetadataFileNameProvider, | |||||
| new BlockingMetadataBootstrappingGuard<>( | |||||
| metadataLoader, metadataParser, MapBackedMetadataContainer.byRegionCode())); | |||||
| } | |||||
| @Override | |||||
| public PhoneMetadata getMetadataForRegion(String regionCode) { | |||||
| if (!GeoEntityUtility.isGeoEntity(regionCode)) { | |||||
| throw new IllegalArgumentException(regionCode + " region code is a non-geo entity"); | |||||
| } | |||||
| return bootstrappingGuard | |||||
| .getOrBootstrap(phoneMetadataFileNameProvider.getFor(regionCode)) | |||||
| .getMetadataBy(regionCode); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,35 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| /** | |||||
| * {@link PhoneMetadataFileNameProvider} implementation that returns the same metadata file name for | |||||
| * each key | |||||
| */ | |||||
| public final class SingleFileModeFileNameProvider implements PhoneMetadataFileNameProvider { | |||||
| private final String phoneMetadataFileName; | |||||
| public SingleFileModeFileNameProvider(String phoneMetadataFileName) { | |||||
| this.phoneMetadataFileName = phoneMetadataFileName; | |||||
| } | |||||
| @Override | |||||
| public String getFor(Object key) { | |||||
| return phoneMetadataFileName; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,53 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.internal; | |||||
| import static org.junit.Assert.assertFalse; | |||||
| import static org.junit.Assert.assertTrue; | |||||
| import org.junit.Test; | |||||
| import org.junit.runner.RunWith; | |||||
| import org.junit.runners.JUnit4; | |||||
| @RunWith(JUnit4.class) | |||||
| public class GeoEntityUtilityTest { | |||||
| @Test | |||||
| public void isGeoEntity_shouldReturnTrueForCountryRegionCode() { | |||||
| assertTrue(GeoEntityUtility.isGeoEntity("DE")); | |||||
| } | |||||
| @Test | |||||
| public void isGeoEntity_shouldReturnFalseForWorldRegionCode() { | |||||
| assertFalse(GeoEntityUtility.isGeoEntity("001")); | |||||
| } | |||||
| @Test | |||||
| public void isGeoEntity_shouldReturnTrueForCountryCallingCode() { | |||||
| assertTrue(GeoEntityUtility.isGeoEntity(41)); | |||||
| } | |||||
| @Test | |||||
| public void isGeoEntity_shouldReturnFalseForInternationalSharedCostServiceCallingCode() { | |||||
| assertFalse(GeoEntityUtility.isGeoEntity(808)); | |||||
| } | |||||
| @Test | |||||
| public void isGeoEntity_shouldReturnFalseForNonExistingCountryCallingCode() { | |||||
| assertFalse(GeoEntityUtility.isGeoEntity(111111111)); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,21 @@ | |||||
| package com.google.i18n.phonenumbers.metadata; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection; | |||||
| import java.io.ByteArrayInputStream; | |||||
| import java.io.ByteArrayOutputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.io.ObjectOutputStream; | |||||
| public class PhoneMetadataCollectionUtil { | |||||
| public static InputStream toInputStream(PhoneMetadataCollection metadata) throws IOException { | |||||
| ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | |||||
| ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); | |||||
| metadata.writeExternal(objectOutputStream); | |||||
| objectOutputStream.flush(); | |||||
| InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); | |||||
| objectOutputStream.close(); | |||||
| return inputStream; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,98 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.init; | |||||
| import static java.nio.charset.StandardCharsets.UTF_8; | |||||
| import static org.junit.Assert.assertEquals; | |||||
| import static org.junit.Assert.assertThrows; | |||||
| import static org.junit.Assert.assertTrue; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection; | |||||
| import com.google.i18n.phonenumbers.metadata.PhoneMetadataCollectionUtil; | |||||
| import java.io.ByteArrayInputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.Collection; | |||||
| import org.junit.Test; | |||||
| import org.junit.function.ThrowingRunnable; | |||||
| import org.junit.runner.RunWith; | |||||
| import org.junit.runners.JUnit4; | |||||
| @RunWith(JUnit4.class) | |||||
| public final class MetadataParserTest { | |||||
| private static final MetadataParser metadataParser = MetadataParser.newStrictParser(); | |||||
| @Test | |||||
| public void parse_shouldThrowExceptionForNullInput() { | |||||
| assertThrows( | |||||
| IllegalArgumentException.class, | |||||
| new ThrowingRunnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| metadataParser.parse(null); | |||||
| } | |||||
| }); | |||||
| } | |||||
| @Test | |||||
| public void parse_shouldThrowExceptionForEmptyInput() { | |||||
| final InputStream emptyInput = new ByteArrayInputStream(new byte[0]); | |||||
| assertThrows( | |||||
| IllegalStateException.class, | |||||
| new ThrowingRunnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| metadataParser.parse(emptyInput); | |||||
| } | |||||
| }); | |||||
| } | |||||
| @Test | |||||
| public void parse_shouldThrowExceptionForInvalidInput() { | |||||
| final InputStream invalidInput = new ByteArrayInputStream("Some random input".getBytes(UTF_8)); | |||||
| assertThrows( | |||||
| IllegalStateException.class, | |||||
| new ThrowingRunnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| metadataParser.parse(invalidInput); | |||||
| } | |||||
| }); | |||||
| } | |||||
| @Test | |||||
| public void parse_shouldParseValidInput() throws IOException { | |||||
| InputStream input = PhoneMetadataCollectionUtil.toInputStream( | |||||
| PhoneMetadataCollection.newBuilder() | |||||
| .addMetadata(PhoneMetadata.newBuilder().setId("id").build())); | |||||
| Collection<PhoneMetadata> actual = metadataParser.parse(input); | |||||
| assertEquals(1, actual.size()); | |||||
| } | |||||
| @Test | |||||
| public void parse_shouldReturnEmptyCollectionForNullInput() { | |||||
| Collection<PhoneMetadata> actual = MetadataParser.newLenientParser().parse(null); | |||||
| assertTrue(actual.isEmpty()); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,117 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import static org.junit.Assert.assertThrows; | |||||
| import static org.mockito.ArgumentMatchers.any; | |||||
| import static org.mockito.Mockito.only; | |||||
| import static org.mockito.Mockito.times; | |||||
| import static org.mockito.Mockito.verify; | |||||
| import static org.mockito.Mockito.when; | |||||
| import com.google.i18n.phonenumbers.MetadataLoader; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection; | |||||
| import com.google.i18n.phonenumbers.metadata.PhoneMetadataCollectionUtil; | |||||
| import com.google.i18n.phonenumbers.metadata.init.MetadataParser; | |||||
| import java.io.IOException; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| import java.util.concurrent.Callable; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| import org.junit.Assert; | |||||
| import org.junit.Before; | |||||
| import org.junit.Rule; | |||||
| import org.junit.Test; | |||||
| import org.junit.function.ThrowingRunnable; | |||||
| import org.junit.runner.RunWith; | |||||
| import org.junit.runners.JUnit4; | |||||
| import org.mockito.Mock; | |||||
| import org.mockito.junit.MockitoJUnit; | |||||
| import org.mockito.junit.MockitoRule; | |||||
| @RunWith(JUnit4.class) | |||||
| public class BlockingMetadataBootstrappingGuardTest { | |||||
| private static final String PHONE_METADATA_FILE = "some metadata file"; | |||||
| private static final PhoneMetadataCollection PHONE_METADATA = | |||||
| PhoneMetadataCollection.newBuilder() | |||||
| .addMetadata(PhoneMetadata.newBuilder().setId("id").build()); | |||||
| @Rule public final MockitoRule mockito = MockitoJUnit.rule(); | |||||
| @Mock private MetadataLoader metadataLoader; | |||||
| @Mock private MetadataContainer metadataContainer; | |||||
| private BlockingMetadataBootstrappingGuard<MetadataContainer> bootstrappingGuard; | |||||
| @Before | |||||
| public void setUp() throws IOException { | |||||
| when(metadataLoader.loadMetadata(PHONE_METADATA_FILE)) | |||||
| .thenReturn(PhoneMetadataCollectionUtil.toInputStream(PHONE_METADATA)); | |||||
| bootstrappingGuard = | |||||
| new BlockingMetadataBootstrappingGuard<>( | |||||
| metadataLoader, MetadataParser.newStrictParser(), metadataContainer); | |||||
| } | |||||
| @Test | |||||
| public void getOrBootstrap_shouldInvokeBootstrappingOnlyOnce() { | |||||
| bootstrappingGuard.getOrBootstrap(PHONE_METADATA_FILE); | |||||
| bootstrappingGuard.getOrBootstrap(PHONE_METADATA_FILE); | |||||
| verify(metadataLoader, times(1)).loadMetadata(PHONE_METADATA_FILE); | |||||
| verify(metadataContainer, only()).accept(any(PhoneMetadata.class)); | |||||
| } | |||||
| @Test | |||||
| public void getOrBootstrap_shouldIncludeFileNameInExceptionOnFailure() { | |||||
| when(metadataLoader.loadMetadata(PHONE_METADATA_FILE)).thenReturn(null); | |||||
| ThrowingRunnable throwingRunnable = | |||||
| new ThrowingRunnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| bootstrappingGuard.getOrBootstrap(PHONE_METADATA_FILE); | |||||
| } | |||||
| }; | |||||
| IllegalStateException exception = assertThrows(IllegalStateException.class, throwingRunnable); | |||||
| Assert.assertTrue(exception.getMessage().contains(PHONE_METADATA_FILE)); | |||||
| } | |||||
| @Test | |||||
| public void getOrBootstrap_shouldInvokeBootstrappingOnlyOnceWhenThreadsCallItAtTheSameTime() | |||||
| throws InterruptedException { | |||||
| ExecutorService executorService = Executors.newFixedThreadPool(2); | |||||
| List<BootstrappingRunnable> runnables = new ArrayList<>(); | |||||
| runnables.add(new BootstrappingRunnable()); | |||||
| runnables.add(new BootstrappingRunnable()); | |||||
| executorService.invokeAll(runnables); | |||||
| verify(metadataLoader, times(1)).loadMetadata(PHONE_METADATA_FILE); | |||||
| verify(metadataContainer, only()).accept(any(PhoneMetadata.class)); | |||||
| } | |||||
| private class BootstrappingRunnable implements Callable<MetadataContainer> { | |||||
| @Override | |||||
| public MetadataContainer call() { | |||||
| return bootstrappingGuard.getOrBootstrap(PHONE_METADATA_FILE); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,78 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import static org.junit.Assert.assertNull; | |||||
| import static org.junit.Assert.assertSame; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import com.google.i18n.phonenumbers.internal.GeoEntityUtility; | |||||
| import org.junit.Before; | |||||
| import org.junit.Test; | |||||
| import org.junit.runner.RunWith; | |||||
| import org.junit.runners.JUnit4; | |||||
| @RunWith(JUnit4.class) | |||||
| public class CompositeMetadataContainerTest { | |||||
| private static final String REGION_CODE = "US"; | |||||
| private static final Integer COUNTRY_CODE = 1; | |||||
| private static final PhoneMetadata PHONE_METADATA_WITH_REGION_CODE = | |||||
| PhoneMetadata.newBuilder().setId(REGION_CODE).setCountryCode(COUNTRY_CODE); | |||||
| private static final PhoneMetadata PHONE_METADATA_WITH_COUNTRY_CODE = | |||||
| PhoneMetadata.newBuilder() | |||||
| .setId(GeoEntityUtility.REGION_CODE_FOR_NON_GEO_ENTITIES) | |||||
| .setCountryCode(COUNTRY_CODE); | |||||
| private CompositeMetadataContainer metadataContainer; | |||||
| @Before | |||||
| public void setUp() { | |||||
| metadataContainer = new CompositeMetadataContainer(); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnNullForNonExistingRegionCode() { | |||||
| assertNull(metadataContainer.getMetadataBy(REGION_CODE)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnMetadataForExistingRegionCode() { | |||||
| metadataContainer.accept(PHONE_METADATA_WITH_REGION_CODE); | |||||
| assertSame(PHONE_METADATA_WITH_REGION_CODE, metadataContainer.getMetadataBy(REGION_CODE)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnNullForNonExistingCountryCode() { | |||||
| assertNull(metadataContainer.getMetadataBy(COUNTRY_CODE)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnMetadataForExistingCountryCode() { | |||||
| metadataContainer.accept(PHONE_METADATA_WITH_COUNTRY_CODE); | |||||
| assertSame(PHONE_METADATA_WITH_COUNTRY_CODE, metadataContainer.getMetadataBy(COUNTRY_CODE)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnNullForExistingCountryCodeOfGeoRegion() { | |||||
| metadataContainer.accept(PHONE_METADATA_WITH_REGION_CODE); | |||||
| assertNull(metadataContainer.getMetadataBy(COUNTRY_CODE)); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,74 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import static org.junit.Assert.assertNull; | |||||
| import static org.junit.Assert.assertSame; | |||||
| import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; | |||||
| import org.junit.Test; | |||||
| import org.junit.runner.RunWith; | |||||
| import org.junit.runners.JUnit4; | |||||
| @RunWith(JUnit4.class) | |||||
| public class MapBackedMetadataContainerTest { | |||||
| private static final String REGION_CODE = "US"; | |||||
| private static final Integer COUNTRY_CODE = 41; | |||||
| private static final PhoneMetadata PHONE_METADATA = | |||||
| PhoneMetadata.newBuilder().setId(REGION_CODE).setCountryCode(COUNTRY_CODE); | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnNullForNullRegionCode() { | |||||
| assertNull(MapBackedMetadataContainer.byRegionCode().getMetadataBy(null)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnNullForNonExistingRegionCode() { | |||||
| assertNull(MapBackedMetadataContainer.byRegionCode().getMetadataBy(REGION_CODE)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnMetadataForExistingRegionCode() { | |||||
| MapBackedMetadataContainer<String> metadataContainer = | |||||
| MapBackedMetadataContainer.byRegionCode(); | |||||
| metadataContainer.accept(PHONE_METADATA); | |||||
| assertSame(PHONE_METADATA, metadataContainer.getMetadataBy(REGION_CODE)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnNullForNullCountryCode() { | |||||
| assertNull(MapBackedMetadataContainer.byCountryCallingCode().getMetadataBy(null)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnNullForNonExistingCountryCode() { | |||||
| assertNull(MapBackedMetadataContainer.byCountryCallingCode().getMetadataBy(COUNTRY_CODE)); | |||||
| } | |||||
| @Test | |||||
| public void getMetadataBy_shouldReturnMetadataForExistingCountryCode() { | |||||
| MapBackedMetadataContainer<Integer> metadataContainer = | |||||
| MapBackedMetadataContainer.byCountryCallingCode(); | |||||
| metadataContainer.accept(PHONE_METADATA); | |||||
| assertSame(PHONE_METADATA, metadataContainer.getMetadataBy(COUNTRY_CODE)); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,51 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import static org.junit.Assert.assertEquals; | |||||
| import static org.junit.Assert.assertThrows; | |||||
| import org.junit.Test; | |||||
| import org.junit.function.ThrowingRunnable; | |||||
| import org.junit.runner.RunWith; | |||||
| import org.junit.runners.JUnit4; | |||||
| @RunWith(JUnit4.class) | |||||
| public final class MultiFileModeFileNameProviderTest { | |||||
| private final PhoneMetadataFileNameProvider metadataFileNameProvider = | |||||
| new MultiFileModeFileNameProvider("some/file"); | |||||
| @Test | |||||
| public void getFor_shouldAppendKeyToTheBase() { | |||||
| String metadataFileName = metadataFileNameProvider.getFor("key1"); | |||||
| assertEquals("some/file_key1", metadataFileName); | |||||
| } | |||||
| @Test | |||||
| public void getFor_shouldThrowExceptionForNonAlphanumericKey() { | |||||
| assertThrows( | |||||
| IllegalArgumentException.class, | |||||
| new ThrowingRunnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| metadataFileNameProvider.getFor("\tkey1\n"); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,37 @@ | |||||
| /* | |||||
| * Copyright (C) 2022 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.metadata.source; | |||||
| import static org.junit.Assert.assertEquals; | |||||
| import org.junit.Test; | |||||
| import org.junit.runner.RunWith; | |||||
| import org.junit.runners.JUnit4; | |||||
| @RunWith(JUnit4.class) | |||||
| public final class SingleFileModeFileNameProviderTest { | |||||
| private final PhoneMetadataFileNameProvider metadataFileNameProvider = | |||||
| new SingleFileModeFileNameProvider("some/file"); | |||||
| @Test | |||||
| public void getFor_shouldReturnTheFileNameBase() { | |||||
| String metadataFileName = metadataFileNameProvider.getFor("key1"); | |||||
| assertEquals("some/file", metadataFileName); | |||||
| } | |||||
| } | |||||