• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
20 import com.google.auto.value.AutoValue;
21 import com.google.common.collect.ImmutableList;
22 import java.io.IOException;
23 import java.io.Reader;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.util.ArrayList;
27 import java.util.Comparator;
28 import java.util.List;
29 import java.util.Optional;
30 import java.util.function.BiConsumer;
31 
32 /**
33  * A CSV schema is a combination of a key marshaller and table columns. A CSV schema defines a
34  * CSV table with key columns, followed by non-key columns.
35  */
36 @AutoValue
37 public abstract class CsvSchema<K> {
38   /**
39    * Returns a schema for a CSV file using the given marshaller to define key columns, and a table
40    * schema to define any additional columns in a row.
41    */
of(CsvKeyMarshaller<K> marshaller, Schema columns)42   public static <K> CsvSchema<K> of(CsvKeyMarshaller<K> marshaller, Schema columns) {
43     return new AutoValue_CsvSchema<>(marshaller, columns);
44   }
45 
46   /** The marshaller defining table keys and how they are serialized in CSV. */
keyMarshaller()47   public abstract CsvKeyMarshaller<K> keyMarshaller();
48 
49   /** The table schema defining non-key columns in the table. */
columns()50   public abstract Schema columns();
51 
52   /** Returns the ordering for keys in the CSV table, as defined by the key marshaller. */
rowOrdering()53   public Optional<Comparator<K>> rowOrdering() {
54     return keyMarshaller().ordering();
55   }
56 
57   /**
58    * Returns the ordering for additional non-key columns in the CSV table as defined by the table
59    * schema.
60    */
columnOrdering()61   public Comparator<Column<?>> columnOrdering() {
62     return columns().ordering();
63   }
64 
65   /**
66    * Extracts the non-key columns of a table from the header row. The header row is expected to
67    * contain the names of all columns (including key columns) in the CSV table and this method
68    * verifies that the key columns are present as expected before resolving the non-key columns
69    * in order.
70    */
parseHeader(List<String> header)71   public ImmutableList<Column<?>> parseHeader(List<String> header) {
72     int hsize = keyMarshaller().getColumns().size();
73     checkArgument(header.size() >= hsize, "CSV header too short: %s", header);
74     checkArgument(header.subList(0, hsize).equals(keyMarshaller().getColumns()),
75         "Invalid CSV header: %s", header);
76     ImmutableList.Builder<Column<?>> columns = ImmutableList.builder();
77     header.subList(hsize, header.size()).forEach(s -> columns.add(columns().getColumn(s)));
78     return columns.build();
79   }
80 
81   /** Parses a row from a CSV table containing unescaped values. */
parseRow( ImmutableList<Column<?>> columns, List<String> row, BiConsumer<K, List<Assignment<?>>> fn)82   public void parseRow(
83       ImmutableList<Column<?>> columns, List<String> row, BiConsumer<K, List<Assignment<?>>> fn) {
84     int hsize = keyMarshaller().getColumns().size();
85     checkArgument(row.size() >= hsize, "CSV row too short: %s", row);
86     K key = keyMarshaller().deserialize(row.subList(0, hsize));
87     List<Assignment<?>> rowAssignments = new ArrayList<>();
88     for (int n = 0; n < row.size() - hsize; n++) {
89       Column<?> c = columns.get(n);
90       rowAssignments.add(
91           Assignment.ofOptional(c, Optional.ofNullable(c.parse(row.get(n + hsize)))));
92     }
93     fn.accept(key, rowAssignments);
94   }
95 
load(Path file)96   public CsvTable<K> load(Path file) throws IOException {
97     if (!Files.exists(file)) {
98       return CsvTable.builder(this).build();
99     }
100     try (Reader csv = Files.newBufferedReader(file)) {
101       return CsvTable.importCsv(this, csv);
102     }
103   }
104 
load(Reader reader)105   public CsvTable<K> load(Reader reader) throws IOException {
106     return CsvTable.importCsv(this, reader);
107   }
108 }
109