1 /*
2  * Copyright (C) 2009 The Android Open Source Project
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 
17 package com.android.networkrecommendation.config;
18 
19 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.util.List;
22 
23 /**
24  * Utilities for parsing and serializing Comma-Separated Value data.
25  * See http://en.wikipedia.org/wiki/Comma-separated_values and
26  * http://tools.ietf.org/html/rfc4180 for details of the format.
27  */
28 public class Csv {
29     /** Field delimiter.  The C in CSV. */
30     public static final String COMMA = ",";
31 
32     /** Record delimiter.  "Proper" CSV uses CR-LF, but that would be annoying. */
33     public static final String NEWLINE = "\n";
34 
35     /**
36      * Appends a single value to an output string.  If the value
37      * contains quotes, commas, newlines, or leading or trailing whitespace,
38      * the value will be written in quotes (with internal quotes doubled).
39      * Newlines are converted to standard CR-LF form.  This function does not
40      * write field delimiters -- append {@link COMMA} yourself between values.
41      *
42      * @param value to write, quoted as necessary; must be non-null.
43      * @param output to append (possibly quoted) value to
44      * @throws java.io.IOException if writing to 'output' fails
45      */
writeValue(String value, Appendable output)46     public static void writeValue(String value, Appendable output) throws IOException {
47         int len = value.length();
48         if (len == 0) return;
49 
50         char first = value.charAt(0);
51         char last = value.charAt(len - 1);
52         if (first != ' ' && first != '\t' && last != ' ' && last != '\t' &&
53                 value.indexOf('"') < 0 && value.indexOf(',') < 0 &&
54                 value.indexOf('\r') < 0 && value.indexOf('\n') < 0) {
55             // No quoting needed.
56             output.append(value);
57             return;
58         }
59 
60         output.append('"').append(value.replace("\"", "\"\"")).append('"');
61     }
62 
63     /**
64      * Parse a record of comma separated values from an input file.
65      * May read multiple physical lines if values contain embedded newlines.
66      *
67      * @param reader to read one or more physical lines from
68      * @param out array to append unquoted CSV values to
69      * @return true if values were read, false on EOF
70      * @throws java.io.IOException if reading from 'reader' fails
71      */
parseLine(BufferedReader reader, List<String> out)72     public static boolean parseLine(BufferedReader reader, List<String> out) throws IOException {
73         String text = reader.readLine();
74         if (text == null) return false;
75 
76         int pos = 0;
77         do {
78             StringBuilder buf = new StringBuilder();
79             int comma;
80             for (;;) {
81                 comma = text.indexOf(',', pos);
82                 int quote = text.indexOf('"', pos);
83                 if (quote == -1 || (comma != -1 && comma < quote)) break;
84 
85                 if (pos > 0 && text.charAt(pos - 1) == '"') buf.append('"');
86                 buf.append(text, pos, quote);
87                 while ((quote = text.indexOf('"', (pos = quote + 1))) == -1) {
88                     buf.append(text, pos, text.length()).append('\n');
89                     text = reader.readLine();
90                     if (text == null) {
91                         out.add(buf.toString());
92                         return true;
93                     }
94                     quote = -1;
95                 }
96 
97                 buf.append(text, pos, quote);
98                 pos = quote + 1;
99             }
100 
101             buf.append(text, pos, comma == -1 ? text.length() : comma);
102             out.add(buf.toString());
103             pos = comma + 1;
104         } while (pos > 0);
105         return true;
106     }
107 
Csv()108     private Csv() {}  // Do not instantiate
109 }
110