1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.jdeprscan; 27 28import java.io.PrintStream; 29import java.util.ArrayList; 30import java.util.Arrays; 31import java.util.List; 32import java.util.stream.Collectors; 33 34/** 35 * Utility class for manipulating comma-separated-value (CSV) data. 36 */ 37public class CSV { 38 static String quote(String input) { 39 String result; 40 boolean needQuote = input.contains(","); 41 42 if (input.contains("\"")) { 43 needQuote = true; 44 result = input.replace("\"", "\"\""); 45 } else { 46 result = input; 47 } 48 49 if (needQuote) { 50 return "\"" + result + "\""; 51 } else { 52 return result; 53 } 54 } 55 56 /** 57 * Writes the objects' string representations to the output as a line of CSV. 58 * The objects are converted to String, quoted if necessary, joined with commas, 59 * and are written to the output followed by the line separator string. 60 * 61 * @param out the output destination 62 * @param objs the objects to write 63 */ 64 public static void write(PrintStream out, Object... objs) { 65 out.println(Arrays.stream(objs) 66 .map(Object::toString) 67 .map(CSV::quote) 68 .collect(Collectors.joining(","))); 69 } 70 71 /** 72 * The CSV parser state. 73 */ 74 enum State { 75 START_FIELD, // the start of a field 76 IN_FIELD, // within an unquoted field 77 IN_QFIELD, // within a quoted field 78 END_QFIELD // after the end of a quoted field 79 } 80 81 /** 82 * Splits an input line into a list of strings, handling quoting. 83 * 84 * @param input the input line 85 * @return the resulting list of strings 86 */ 87 public static List<String> split(String input) { 88 List<String> result = new ArrayList<>(); 89 StringBuilder cur = new StringBuilder(); 90 State state = State.START_FIELD; 91 92 for (int i = 0; i < input.length(); i++) { 93 char ch = input.charAt(i); 94 switch (ch) { 95 case ',': 96 switch (state) { 97 case IN_QFIELD: 98 cur.append(','); 99 break; 100 default: 101 result.add(cur.toString()); 102 cur.setLength(0); 103 state = State.START_FIELD; 104 break; 105 } 106 break; 107 case '"': 108 switch (state) { 109 case START_FIELD: 110 state = State.IN_QFIELD; 111 break; 112 case IN_QFIELD: 113 state = State.END_QFIELD; 114 break; 115 case IN_FIELD: 116 throw new CSVParseException("unexpected quote", input, i); 117 case END_QFIELD: 118 cur.append('"'); 119 state = State.IN_QFIELD; 120 break; 121 } 122 break; 123 default: 124 switch (state) { 125 case START_FIELD: 126 state = State.IN_FIELD; 127 break; 128 case IN_FIELD: 129 case IN_QFIELD: 130 break; 131 case END_QFIELD: 132 throw new CSVParseException("extra character after quoted string", 133 input, i); 134 } 135 cur.append(ch); 136 break; 137 } 138 } 139 140 if (state == State.IN_QFIELD) { 141 throw new CSVParseException("unclosed quote", input, input.length()); 142 } 143 144 result.add(cur.toString()); 145 return result; 146 } 147} 148