1+ /*
2+ * Licensed to the Apache Software Foundation (ASF) under one or more
3+ * contributor license agreements. See the NOTICE file distributed with
4+ * this work for additional information regarding copyright ownership.
5+ * The ASF licenses this file to You under the Apache License, Version 2.0
6+ * (the "License"); you may not use this file except in compliance with
7+ * the License. You may obtain a copy of the License at
8+ *
9+ * http://www.apache.org/licenses/LICENSE-2.0
10+ *
11+ * Unless required by applicable law or agreed to in writing, software
12+ * distributed under the License is distributed on an "AS IS" BASIS,
13+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ * See the License for the specific language governing permissions and
15+ * limitations under the License.
16+ */
17+ package org .apache .commons .csv .issues ;
18+
19+ import static org .junit .jupiter .api .Assertions .assertEquals ;
20+
21+ import java .io .IOException ;
22+ import java .io .StringReader ;
23+
24+ import org .apache .commons .csv .CSVFormat ;
25+ import org .apache .commons .csv .CSVParser ;
26+ import org .apache .commons .csv .CSVRecord ;
27+ import org .apache .commons .csv .QuoteMode ;
28+ import org .junit .jupiter .api .Test ;
29+
30+ /**
31+ * Add more tests about null value.
32+ * <p>
33+ * QuoteMode:ALL_NON_NULL (Quotes all non-null fields, null will not be quoted but not null will be quoted). when
34+ * withNullString("NULL"), NULL String value ("NULL") and null value (null) will be formatted as '"NULL",NULL'. So it
35+ * also should be parsed as NULL String value and null value (["NULL", null]), It should be distinguish in parsing. And
36+ * when don't set nullString in CSVFormat, String '"",' should be parsed as "" and null (["", null]) according to null
37+ * will not be quoted but not null will be quoted. QuoteMode:NON_NUMERIC, same as ALL_NON_NULL.
38+ * </p>
39+ * <p>
40+ * This can solve the problem of distinguishing between empty string columns and absent value columns which just like
41+ * Jira CSV-253 to a certain extent.
42+ * </p>
43+ */
44+ public class JiraCsv93Test {
45+ private static Object [] objects1 = {"abc" , "" , null , "a,b,c" , 123 };
46+
47+ private static Object [] objects2 = {"abc" , "NULL" , null , "a,b,c" , 123 };
48+
49+ private void every (final CSVFormat csvFormat , final Object [] objects , final String format , final String [] data )
50+ throws IOException {
51+ final String source = csvFormat .format (objects );
52+ assertEquals (format , csvFormat .format (objects ));
53+ try (final CSVParser csvParser = csvFormat .parse (new StringReader (source ))) {
54+ final CSVRecord csvRecord = csvParser .iterator ().next ();
55+ for (int i = 0 ; i < data .length ; i ++) {
56+ assertEquals (csvRecord .get (i ), data [i ]);
57+ }
58+ }
59+ }
60+
61+ @ Test
62+ public void testWithNotSetNullString () throws IOException {
63+ // @formatter:off
64+ every (CSVFormat .DEFAULT ,
65+ objects1 ,
66+ "abc,,,\" a,b,c\" ,123" ,
67+ new String []{"abc" , "" , "" , "a,b,c" , "123" });
68+ every (CSVFormat .DEFAULT .builder ().setQuoteMode (QuoteMode .ALL ).build (),
69+ objects1 ,
70+ "\" abc\" ,\" \" ,,\" a,b,c\" ,\" 123\" " ,
71+ new String []{"abc" , "" , "" , "a,b,c" , "123" });
72+ every (CSVFormat .DEFAULT .builder ().setQuoteMode (QuoteMode .ALL_NON_NULL ).build (),
73+ objects1 ,
74+ "\" abc\" ,\" \" ,,\" a,b,c\" ,\" 123\" " ,
75+ new String []{"abc" , "" , null , "a,b,c" , "123" });
76+ every (CSVFormat .DEFAULT .builder ().setQuoteMode (QuoteMode .MINIMAL ).build (),
77+ objects1 ,
78+ "abc,,,\" a,b,c\" ,123" ,
79+ new String []{"abc" , "" , "" , "a,b,c" , "123" });
80+ every (CSVFormat .DEFAULT .builder ().setEscape ('?' ).setQuoteMode (QuoteMode .NONE ).build (),
81+ objects1 ,
82+ "abc,,,a?,b?,c,123" ,
83+ new String []{"abc" , "" , "" , "a,b,c" , "123" });
84+ every (CSVFormat .DEFAULT .builder ().setQuoteMode (QuoteMode .NON_NUMERIC ).build (),
85+ objects1 ,
86+ "\" abc\" ,\" \" ,,\" a,b,c\" ,123" ,
87+ new String []{"abc" , "" , null , "a,b,c" , "123" });
88+ // @formatter:on
89+ }
90+
91+ @ Test
92+ public void testWithSetNullStringEmptyString () throws IOException {
93+ // @formatter:off
94+ every (CSVFormat .DEFAULT .builder ().setNullString ("" ).build (),
95+ objects1 ,
96+ "abc,,,\" a,b,c\" ,123" ,
97+ new String []{"abc" , null , null , "a,b,c" , "123" });
98+ every (CSVFormat .DEFAULT .builder ().setNullString ("" ).setQuoteMode (QuoteMode .ALL ).build (),
99+ objects1 ,
100+ "\" abc\" ,\" \" ,\" \" ,\" a,b,c\" ,\" 123\" " ,
101+ new String []{"abc" , null , null , "a,b,c" , "123" });
102+ every (CSVFormat .DEFAULT .builder ().setNullString ("" ).setQuoteMode (QuoteMode .ALL_NON_NULL ).build (),
103+ objects1 ,
104+ "\" abc\" ,\" \" ,,\" a,b,c\" ,\" 123\" " ,
105+ new String []{"abc" , "" , null , "a,b,c" , "123" });
106+ every (CSVFormat .DEFAULT .builder ().setNullString ("" ).setQuoteMode (QuoteMode .MINIMAL ).build (),
107+ objects1 ,
108+ "abc,,,\" a,b,c\" ,123" ,
109+ new String []{"abc" , null , null , "a,b,c" , "123" });
110+ every (CSVFormat .DEFAULT .builder ().setNullString ("" ).setEscape ('?' ).setQuoteMode (QuoteMode .NONE ).build (),
111+ objects1 ,
112+ "abc,,,a?,b?,c,123" ,
113+ new String []{"abc" , null , null , "a,b,c" , "123" });
114+ every (CSVFormat .DEFAULT .builder ().setNullString ("" ).setQuoteMode (QuoteMode .NON_NUMERIC ).build (),
115+ objects1 ,
116+ "\" abc\" ,\" \" ,,\" a,b,c\" ,123" ,
117+ new String []{"abc" , "" , null , "a,b,c" , "123" });
118+ // @formatter:on
119+ }
120+
121+ @ Test
122+ public void testWithSetNullStringNULL () throws IOException {
123+ // @formatter:off
124+ every (CSVFormat .DEFAULT .builder ().setNullString ("NULL" ).build (),
125+ objects2 ,
126+ "abc,NULL,NULL,\" a,b,c\" ,123" ,
127+ new String []{"abc" , null , null , "a,b,c" , "123" });
128+ every (CSVFormat .DEFAULT .builder ().setNullString ("NULL" ).setQuoteMode (QuoteMode .ALL ).build (),
129+ objects2 ,
130+ "\" abc\" ,\" NULL\" ,\" NULL\" ,\" a,b,c\" ,\" 123\" " ,
131+ new String []{"abc" , null , null , "a,b,c" , "123" });
132+ every (CSVFormat .DEFAULT .builder ().setNullString ("NULL" ).setQuoteMode (QuoteMode .ALL_NON_NULL ).build (),
133+ objects2 ,
134+ "\" abc\" ,\" NULL\" ,NULL,\" a,b,c\" ,\" 123\" " ,
135+ new String []{"abc" , "NULL" , null , "a,b,c" , "123" });
136+ every (CSVFormat .DEFAULT .builder ().setNullString ("NULL" ).setQuoteMode (QuoteMode .MINIMAL ).build (),
137+ objects2 ,
138+ "abc,NULL,NULL,\" a,b,c\" ,123" ,
139+ new String []{"abc" , null , null , "a,b,c" , "123" });
140+ every (CSVFormat .DEFAULT .builder ().setNullString ("NULL" ).setEscape ('?' ).setQuoteMode (QuoteMode .NONE ).build (),
141+ objects2 ,
142+ "abc,NULL,NULL,a?,b?,c,123" ,
143+ new String []{"abc" , null , null , "a,b,c" , "123" });
144+ every (CSVFormat .DEFAULT .builder ().setNullString ("NULL" ).setQuoteMode (QuoteMode .NON_NUMERIC ).build (),
145+ objects2 ,
146+ "\" abc\" ,\" NULL\" ,NULL,\" a,b,c\" ,123" ,
147+ new String []{"abc" , "NULL" , null , "a,b,c" , "123" });
148+ // @formatter:on
149+ }
150+ }
0 commit comments