1 /* 2 * Copyright (C) 2017 The Libphonenumber Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.i18n.phonenumbers.metadata.table; 17 18 import static com.google.common.base.Preconditions.checkArgument; 19 import static com.google.common.collect.ImmutableBiMap.toImmutableBiMap; 20 import static java.util.function.Function.identity; 21 22 import com.google.auto.value.AutoValue; 23 import com.google.common.collect.ImmutableBiMap; 24 import com.google.i18n.phonenumbers.metadata.i18n.PhoneRegion; 25 import com.google.i18n.phonenumbers.metadata.i18n.SimpleLanguageTag; 26 import java.util.Set; 27 import java.util.function.Function; 28 29 /** A group of {@link RangeTable} columns. */ 30 @AutoValue 31 public abstract class ColumnGroup<K, T extends Comparable<T>> { 32 /** 33 * Returns a group for columns with the same type as the given "prototype" column and which has a 34 * a prefix that's the name of the prototype. Suffix values are parsed using the given function. 35 */ of( Column<T> prototype, Function<String, K> parseFn)36 public static <K, T extends Comparable<T>> ColumnGroup<K, T> of( 37 Column<T> prototype, Function<String, K> parseFn) { 38 return new AutoValue_ColumnGroup<>(prototype, parseFn); 39 } 40 41 /** Returns a group for the specified prototype column keyed by {@link PhoneRegion}. */ byRegion( Column<T> prototype)42 public static <T extends Comparable<T>> ColumnGroup<PhoneRegion, T> byRegion( 43 Column<T> prototype) { 44 return of(prototype, PhoneRegion::of); 45 } 46 47 /** Returns a group for the specified prototype column keyed by {@link SimpleLanguageTag}. */ byLanguage( Column<T> prototype)48 public static <T extends Comparable<T>> ColumnGroup<SimpleLanguageTag, T> byLanguage( 49 Column<T> prototype) { 50 return of(prototype, SimpleLanguageTag::of); 51 } 52 53 // Internal use only. prototype()54 abstract Column<T> prototype(); parseFn()55 abstract Function<String, K> parseFn(); 56 57 /** Returns the column for a specified key. */ getColumn(K key)58 public Column<T> getColumn(K key) { 59 // The reason this does not just call "prototype().fromPrototype(...)" is that the key may not 60 // be parsable by the function just because it's the "right" type. This allows people to pass 61 // in a function that limits columns to some subset of the domain (e.g. a subset of region 62 // codes). 63 return getColumnFromId(key.toString()); 64 } 65 66 /** Returns the column for a specified ID string. */ getColumnFromId(String id)67 public Column<T> getColumnFromId(String id) { 68 try { 69 Object unused = parseFn().apply(id); 70 } catch (RuntimeException e) { 71 throw new IllegalArgumentException( 72 String.format("invalid column %s, not in group: %s", id, this), e); 73 } 74 return prototype().fromPrototype(id); 75 } 76 77 /** Returns the key of a column in this group. */ 78 @SuppressWarnings("unchecked") getKey(Column<?> c)79 public K getKey(Column<?> c) { 80 checkArgument(c.isIn(this), "column %s in not group %s", c, this); 81 // Cast is safe since any column in this group is a Column<T>. 82 return extractKey((Column<T>) c); 83 } 84 85 /** Returns a bidirectional mapping from group key to column, for columns in this group. */ 86 @SuppressWarnings("unchecked") extractGroupColumns(Set<Column<?>> columns)87 public ImmutableBiMap<K, Column<T>> extractGroupColumns(Set<Column<?>> columns) { 88 return columns.stream() 89 .filter(c -> c.isIn(this)) 90 // Cast is safe since any column in this group is a Column<T>. 91 .map(c -> (Column<T>) c) 92 .collect(toImmutableBiMap(this::extractKey, identity())); 93 } 94 95 // Assumes we've already verified that the column is in this group. extractKey(Column<T> column)96 private K extractKey(Column<T> column) { 97 String name = column.getName(); 98 return parseFn().apply(name.substring(name.lastIndexOf(':') + 1)); 99 } 100 } 101