@@ -90,7 +90,11 @@ public class CsvEncoder
90
90
protected boolean _cfgAlwaysQuoteStrings ;
91
91
92
92
protected boolean _cfgAlwaysQuoteEmptyStrings ;
93
-
93
+
94
+ protected boolean _cfgEscapeQuoteCharWithEscapeChar ;
95
+
96
+ protected final char _cfgQuoteCharEscapeChar ;
97
+
94
98
/*
95
99
/**********************************************************
96
100
/* Output state
@@ -169,6 +173,7 @@ public CsvEncoder(IOContext ctxt, int csvFeatures, Writer out, CsvSchema schema)
169
173
_cfgIncludeMissingTail = !CsvGenerator .Feature .OMIT_MISSING_TAIL_COLUMNS .enabledIn (_csvFeatures );
170
174
_cfgAlwaysQuoteStrings = CsvGenerator .Feature .ALWAYS_QUOTE_STRINGS .enabledIn (csvFeatures );
171
175
_cfgAlwaysQuoteEmptyStrings = CsvGenerator .Feature .ALWAYS_QUOTE_EMPTY_STRINGS .enabledIn (csvFeatures );
176
+ _cfgEscapeQuoteCharWithEscapeChar = CsvGenerator .Feature .ESCAPE_QUOTE_CHAR_WITH_ESCAPE_CHAR .enabledIn (csvFeatures );
172
177
173
178
_outputBuffer = ctxt .allocConcatBuffer ();
174
179
_bufferRecyclable = true ;
@@ -187,6 +192,12 @@ public CsvEncoder(IOContext ctxt, int csvFeatures, Writer out, CsvSchema schema)
187
192
_cfgMinSafeChar = _calcSafeChar ();
188
193
189
194
_cfgMaxQuoteCheckChars = MAX_QUOTE_CHECK ;
195
+
196
+ _cfgQuoteCharEscapeChar = _getQuoteCharEscapeChar (
197
+ _cfgEscapeQuoteCharWithEscapeChar ,
198
+ _cfgQuoteCharacter ,
199
+ _cfgEscapeCharacter
200
+ );
190
201
}
191
202
192
203
public CsvEncoder (CsvEncoder base , CsvSchema newSchema )
@@ -197,6 +208,7 @@ public CsvEncoder(CsvEncoder base, CsvSchema newSchema)
197
208
_cfgIncludeMissingTail = base ._cfgIncludeMissingTail ;
198
209
_cfgAlwaysQuoteStrings = base ._cfgAlwaysQuoteStrings ;
199
210
_cfgAlwaysQuoteEmptyStrings = base ._cfgAlwaysQuoteEmptyStrings ;
211
+ _cfgEscapeQuoteCharWithEscapeChar = base ._cfgEscapeQuoteCharWithEscapeChar ;
200
212
201
213
_outputBuffer = base ._outputBuffer ;
202
214
_bufferRecyclable = base ._bufferRecyclable ;
@@ -212,8 +224,33 @@ public CsvEncoder(CsvEncoder base, CsvSchema newSchema)
212
224
_cfgNullValue = newSchema .getNullValueOrEmpty ();
213
225
_cfgMinSafeChar = _calcSafeChar ();
214
226
_columnCount = newSchema .size ();
215
- }
216
-
227
+ _cfgQuoteCharEscapeChar = _getQuoteCharEscapeChar (
228
+ base ._cfgEscapeQuoteCharWithEscapeChar ,
229
+ newSchema .getQuoteChar (),
230
+ newSchema .getEscapeChar ()
231
+ );
232
+ }
233
+
234
+ private final char _getQuoteCharEscapeChar (
235
+ final boolean escapeQuoteCharWithEscapeChar ,
236
+ final int quoteCharacter ,
237
+ final int escapeCharacter ) {
238
+
239
+ final char quoteEscapeChar ;
240
+
241
+ if (_cfgEscapeQuoteCharWithEscapeChar && _cfgEscapeCharacter > 0 ) {
242
+ quoteEscapeChar = (char ) _cfgEscapeCharacter ;
243
+ }
244
+ else if (_cfgQuoteCharacter > 0 ) {
245
+ quoteEscapeChar = (char ) _cfgQuoteCharacter ;
246
+ }
247
+ else {
248
+ quoteEscapeChar = '\\' ;
249
+ }
250
+
251
+ return quoteEscapeChar ;
252
+ }
253
+
217
254
private final int _calcSafeChar ()
218
255
{
219
256
// note: quote char may be -1 to signify "no quoting":
@@ -237,6 +274,7 @@ public CsvEncoder overrideFormatFeatures(int feat) {
237
274
_cfgIncludeMissingTail = !CsvGenerator .Feature .OMIT_MISSING_TAIL_COLUMNS .enabledIn (feat );
238
275
_cfgAlwaysQuoteStrings = CsvGenerator .Feature .ALWAYS_QUOTE_STRINGS .enabledIn (feat );
239
276
_cfgAlwaysQuoteEmptyStrings = CsvGenerator .Feature .ALWAYS_QUOTE_EMPTY_STRINGS .enabledIn (feat );
277
+ _cfgEscapeQuoteCharWithEscapeChar = CsvGenerator .Feature .ESCAPE_QUOTE_CHAR_WITH_ESCAPE_CHAR .enabledIn (feat );
240
278
}
241
279
return this ;
242
280
}
@@ -702,7 +740,7 @@ protected void _writeQuoted(String text, char q, int i) throws IOException
702
740
if (_outputTail >= _outputEnd ) {
703
741
_flushBuffer ();
704
742
}
705
- buf [_outputTail ++] = q ;
743
+ buf [_outputTail ++] = _cfgQuoteCharEscapeChar ;
706
744
}
707
745
if (_outputTail >= _outputEnd ) {
708
746
_flushBuffer ();
@@ -724,7 +762,7 @@ private final void _writeLongQuoted(String text, char q) throws IOException
724
762
}
725
763
char c = text .charAt (i );
726
764
if (c == q ) { // double up
727
- _outputBuffer [_outputTail ++] = q ;
765
+ _outputBuffer [_outputTail ++] = _cfgQuoteCharEscapeChar ;
728
766
if (_outputTail >= _outputEnd ) {
729
767
_flushBuffer ();
730
768
}
@@ -782,7 +820,7 @@ protected void _writeQuotedAndEscaped(String text, char q, char esc, int i) thro
782
820
if (_outputTail >= _outputEnd ) {
783
821
_flushBuffer ();
784
822
}
785
- buf [_outputTail ++] = c ;
823
+ buf [_outputTail ++] = ( c == q ) ? _cfgQuoteCharEscapeChar : c ;
786
824
}
787
825
if (_outputTail >= _outputEnd ) {
788
826
_flushBuffer ();
@@ -800,13 +838,14 @@ private final void _writeLongQuotedAndEscaped(String text, char esc) throws IOEx
800
838
final int len = text .length ();
801
839
// NOTE: caller should guarantee quote char is valid (not -1) at this point:
802
840
final char q = (char ) _cfgQuoteCharacter ;
841
+ final char quoteEscape = _cfgEscapeQuoteCharWithEscapeChar ? esc : q ;
803
842
for (int i = 0 ; i < len ; ++i ) {
804
843
if (_outputTail >= _outputEnd ) {
805
844
_flushBuffer ();
806
845
}
807
846
char c = text .charAt (i );
808
847
if ((c == q ) || (c == esc )) { // double up, either way
809
- _outputBuffer [_outputTail ++] = c ;
848
+ _outputBuffer [_outputTail ++] = ( c == q ) ? quoteEscape : c ;
810
849
if (_outputTail >= _outputEnd ) {
811
850
_flushBuffer ();
812
851
}
0 commit comments