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