001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.csv; 018 019import java.io.*; 020 021import org.apache.juneau.marshall.*; 022import org.apache.juneau.marshall.serializer.*; 023 024/** 025 * Specialized writer for serializing CSV. 026 * 027 * <h5 class='section'>Notes:</h5><ul> 028 * <li class='note'> 029 * This class is not intended for external use. 030 * </ul> 031 */ 032@SuppressWarnings({ 033 "java:S3776", // Cognitive complexity acceptable for CSV cell quoting/escaping logic 034 "resource" // Writer resource managed by calling code 035}) 036public class CsvWriter extends SerializerWriter<CsvWriter> { 037 038 /** 039 * Constructor. 040 * 041 * @param out The writer being wrapped. 042 * @param useWhitespace If <jk>true</jk>, tabs and spaces will be used in output. 043 * @param maxIndent The maximum indentation level. 044 * @param quoteChar The quote character to use (i.e. <js>'\''</js> or <js>'"'</js>) 045 * @param trimStrings If <jk>true</jk>, strings will be trimmed before being serialized. 046 * @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form. 047 */ 048 protected CsvWriter(Writer out, boolean useWhitespace, int maxIndent, char quoteChar, boolean trimStrings, UriResolver uriResolver) { 049 super(out, useWhitespace, maxIndent, trimStrings, quoteChar, uriResolver); 050 } 051 052 /** 053 * Writes an entry to the writer. 054 * 055 * <p> 056 * Follows RFC 4180 quoting rules: 057 * <ul> 058 * <li>Values containing commas, double-quote characters, or newlines are enclosed in double quotes. 059 * <li>Double-quote characters within a quoted field are escaped by preceding them with another double-quote. 060 * <li>The literal string {@code null} is written unquoted; the string value {@code "null"} is quoted. 061 * </ul> 062 * 063 * @param value The value to write. 064 * <br>Can be <jk>null</jk> (written as the literal <js>"null"</js>). 065 */ 066 public void writeEntry(Object value) { 067 if (value == null) { 068 w("null"); 069 } else { 070 var s = value.toString(); 071 var mustQuote = false; 072 for (var i = 0; i < s.length() && !mustQuote; i++) { 073 var c = s.charAt(i); 074 if (c == ',' || c == '"' || c == '\r' || c == '\n') 075 mustQuote = true; 076 } 077 if (mustQuote) { 078 w('"'); 079 for (var i = 0; i < s.length(); i++) { 080 var c = s.charAt(i); 081 if (c == '"') 082 w('"'); // RFC 4180: escape embedded quote by doubling it 083 w(c); 084 } 085 w('"'); 086 } else { 087 w(s); 088 } 089 } 090 } 091}