@@ -183,29 +183,71 @@ public static String rowToString(JSONArray ja, char delimiter) {
183183 sb .append (delimiter );
184184 }
185185 Object object = ja .opt (i );
186- if (object != null ) {
187- String string = object .toString ();
188- if (!string .isEmpty () && (string .indexOf (delimiter ) >= 0 ||
189- string .indexOf ('\n' ) >= 0 || string .indexOf ('\r' ) >= 0 ||
190- string .indexOf (0 ) >= 0 || string .charAt (0 ) == '"' )) {
191- sb .append ('"' );
192- int length = string .length ();
193- for (int j = 0 ; j < length ; j += 1 ) {
194- char c = string .charAt (j );
195- if (c >= ' ' && c != '"' ) {
196- sb .append (c );
197- }
198- }
199- sb .append ('"' );
200- } else {
201- sb .append (string );
202- }
203- }
186+ appendRowValue (sb , object , delimiter );
204187 }
205188 sb .append ('\n' );
206189 return sb .toString ();
207190 }
208191
192+ /**
193+ * Append a single row value, quoting it when required by the delimiter or
194+ * content.
195+ *
196+ * @param sb the destination buffer
197+ * @param object the value to append
198+ * @param delimiter the delimiter used between row values
199+ */
200+ private static void appendRowValue (StringBuilder sb , Object object , char delimiter ) {
201+ if (object == null ) {
202+ return ;
203+ }
204+ String string = object .toString ();
205+ if (shouldQuoteValue (string , delimiter )) {
206+ appendQuotedValue (sb , string );
207+ } else {
208+ sb .append (string );
209+ }
210+ }
211+
212+ /**
213+ * Determine whether a row value should be quoted.
214+ *
215+ * @param value the row value to evaluate
216+ * @param delimiter the delimiter used between row values
217+ * @return {@code true} if the value should be quoted
218+ */
219+ private static boolean shouldQuoteValue (String value , char delimiter ) {
220+ if (value .isEmpty ()) {
221+ return false ;
222+ }
223+ boolean containsDelimiter = value .indexOf (delimiter ) >= 0 ;
224+ boolean containsNewline = value .indexOf ('\n' ) >= 0 ;
225+ boolean containsCarriageReturn = value .indexOf ('\r' ) >= 0 ;
226+ boolean containsNullCharacter = value .indexOf (0 ) >= 0 ;
227+ boolean startsWithQuote = value .charAt (0 ) == '"' ;
228+ return containsDelimiter || containsNewline || containsCarriageReturn ||
229+ containsNullCharacter || startsWithQuote ;
230+ }
231+
232+ /**
233+ * Append a row value surrounded by quotes, omitting characters that should
234+ * not appear inside the quoted value.
235+ *
236+ * @param sb the destination buffer
237+ * @param value the value to append
238+ */
239+ private static void appendQuotedValue (StringBuilder sb , String value ) {
240+ sb .append ('"' );
241+ int length = value .length ();
242+ for (int j = 0 ; j < length ; j += 1 ) {
243+ char c = value .charAt (j );
244+ if (c >= ' ' && c != '"' ) {
245+ sb .append (c );
246+ }
247+ }
248+ sb .append ('"' );
249+ }
250+
209251 /**
210252 * Produce a JSONArray of JSONObjects from a comma delimited text string,
211253 * using the first row as a source of names.
0 commit comments