1/******************************************************************************* 2 * Copyright (c) 2009, 2015 Mountainminds GmbH & Co. KG and Contributors 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * Brock Janiczak - initial API and implementation 10 * 11 *******************************************************************************/ 12package org.jacoco.report.csv; 13 14import java.io.IOException; 15import java.io.Writer; 16 17/** 18 * Helper class for writing out CSV or tab delimited files. 19 * <p> 20 * <strong>Example Usage:</strong> 21 * 22 * <pre> 23 * delimitedWriter.writeFields("header1", "header2", ...); 24 * for each line to be written { 25 * delimitedWriter.writeField(value1); 26 * delimitedWriter.writeField(value2); 27 * delimitedWriter.nextLine(); 28 * } 29 * delimitedWriter.close(); 30 * </pre> 31 * 32 * </p> 33 */ 34class DelimitedWriter { 35 private static final String QUOTE = "\""; 36 private static final String ESCAPED_QUOTE = "\"\""; 37 38 private static final char DEFAULT_DELIMITER = ','; 39 private static final String NEW_LINE = System.getProperty("line.separator"); 40 private final char delimiter; 41 private final Writer delegate; 42 private int fieldPosition = 0; 43 44 /** 45 * Creates a new Delimited writer using the default delimiter 46 * 47 * @param delegate 48 * Writer to delegate all writes to 49 */ 50 public DelimitedWriter(final Writer delegate) { 51 this(delegate, DEFAULT_DELIMITER); 52 } 53 54 /** 55 * Creates a new Delimited writer using the default delimiter 56 * 57 * @param delegate 58 * Writer to delegate all writes to 59 * @param delimiter 60 * delimiter to use (usually a comma, tab or space) 61 */ 62 public DelimitedWriter(final Writer delegate, final char delimiter) { 63 this.delegate = delegate; 64 this.delimiter = delimiter; 65 } 66 67 /** 68 * Write multiple fields at once. Values will be auto escaped and quoted as 69 * needed. Each value will be separated using the current delimiter 70 * 71 * @param fields 72 * Values to write 73 * @throws IOException 74 * Error writing to the underlying writer object 75 */ 76 public void write(final String... fields) throws IOException { 77 for (final String field : fields) { 78 write(field); 79 } 80 } 81 82 /** 83 * Write a single value. Values will be auto escaped and quoted as needed. 84 * If this is not the first field of the current line the value will be 85 * prepended with the current delimiter 86 * 87 * @param field 88 * Value to write 89 * @throws IOException 90 * Error writing to the underlying writer object 91 */ 92 public void write(final String field) throws IOException { 93 if (fieldPosition != 0) { 94 delegate.write(delimiter); 95 } 96 delegate.write(escape(field)); 97 fieldPosition++; 98 } 99 100 /** 101 * Write a single integer value. 102 * 103 * @param value 104 * Value to write 105 * @throws IOException 106 * Error writing to the underlying writer object 107 */ 108 public void write(final int value) throws IOException { 109 write(Integer.toString(value)); 110 } 111 112 /** 113 * Write muliple integer values 114 * 115 * @param values 116 * values to write 117 * @throws IOException 118 * Error writing to the underlying writer object 119 */ 120 public void write(final int... values) throws IOException { 121 for (final int value : values) { 122 write(Integer.toString(value)); 123 } 124 } 125 126 /** 127 * Output a new line and advance the writer to the next line. The line 128 * delimiter is the default for the platform. 129 * 130 * @throws IOException 131 * Error writing to the underlying writer object 132 */ 133 public void nextLine() throws IOException { 134 delegate.write(NEW_LINE); 135 fieldPosition = 0; 136 } 137 138 /** 139 * Close the underlying writer object. Once closed all write operations will 140 * fail 141 * 142 * @throws IOException 143 * Error closing the underlying writer object 144 */ 145 public void close() throws IOException { 146 delegate.close(); 147 } 148 149 /** 150 * Escapes any occurrences of the quote character in value by replacing it 151 * with a double quote. Also Quotes the value if a quote or delimiter value 152 * is found. 153 * 154 * @param value 155 * String that needs escaping 156 * @return New string with all values escaped 157 */ 158 private String escape(final String value) { 159 String escapedValue = value; 160 161 // Escape and quote if the source value contains the delimiter 162 // or the quote character 163 if (value.indexOf(QUOTE) != -1 || value.indexOf(delimiter) != -1) { 164 escapedValue = value.replace(QUOTE, ESCAPED_QUOTE); 165 escapedValue = QUOTE + escapedValue + QUOTE; 166 } 167 168 return escapedValue; 169 } 170} 171