Skip to content

Commit da688a7

Browse files
committed
Merge branch '2.9' of github.com:FasterXML/jackson-dataformats-text into 2.9
2 parents 9ef6df6 + e85d314 commit da688a7

File tree

4 files changed

+315
-17
lines changed

4 files changed

+315
-17
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.fasterxml.jackson.dataformat.csv;
2+
3+
import com.fasterxml.jackson.core.SerializableString;
4+
import com.fasterxml.jackson.core.io.CharTypes;
5+
import com.fasterxml.jackson.core.io.CharacterEscapes;
6+
import com.fasterxml.jackson.dataformat.csv.CsvGenerator.Feature;
7+
8+
/**
9+
* Character escapes for CSV. There are multiple types of escapes.
10+
*
11+
* <ul>
12+
* <li>no escapes - return all characters the same way they are defined</li>
13+
* <li>quote escape - return all characters except the quote character which is escaped (backwards compat) </li>
14+
* <li>control escape - same as {@link CharTypes#get7BitOutputEscapes()}, escape all control characters</li>
15+
* <li> control and quote escape - do not double up quote, escape control characters and quote.</li>
16+
* </ul>
17+
*/
18+
public final class CsvCharacterEscapes extends CharacterEscapes
19+
{
20+
21+
private static final long serialVersionUID = 1L;
22+
23+
// No character escapes, every character returned as is.
24+
private static final CsvCharacterEscapes sNoEscapesInstance = new CsvCharacterEscapes(new int[0]);
25+
26+
// Only escape quotes, controlled by {@link Feature#ESCAPE_QUOTE_CHAR_WITH_ESCAPE_CHAR}.
27+
private static final CsvCharacterEscapes sQuoteEscapesInstance;
28+
29+
// Only escape control chars, do *not* escape the quote char. See (@link Feature#ESCAPE_CONTROL_CHARS_WITH_ESCAPE_CHAR}.
30+
private static final CsvCharacterEscapes sControlEscapesInstance;
31+
32+
// Escape control chars and the quote char.
33+
private static final CsvCharacterEscapes sControlQuoteEscapesInstance = new CsvCharacterEscapes(CharacterEscapes.standardAsciiEscapesForJSON());
34+
35+
private static final CsvCharacterEscapes [] sEscapes;
36+
37+
static {
38+
int[] quoteEscapes = new int[(int) '"' + 1];
39+
quoteEscapes[(int) '"'] = '"';
40+
sQuoteEscapesInstance = new CsvCharacterEscapes(quoteEscapes);
41+
42+
int[] controlEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
43+
controlEscapes['"'] = 0; // do not escape ", double it up.
44+
sControlEscapesInstance = new CsvCharacterEscapes(controlEscapes);
45+
46+
sEscapes = new CsvCharacterEscapes[4];
47+
sEscapes[0] = sNoEscapesInstance;
48+
sEscapes[1] = sQuoteEscapesInstance;
49+
sEscapes[2] = sControlEscapesInstance;
50+
sEscapes[3] = sControlQuoteEscapesInstance;
51+
}
52+
53+
54+
private final int[] escapes;
55+
56+
private CsvCharacterEscapes(int[] escapes)
57+
{
58+
this.escapes = escapes;
59+
}
60+
61+
public static CsvCharacterEscapes noEscapesInstance()
62+
{
63+
return sNoEscapesInstance;
64+
}
65+
66+
public static CsvCharacterEscapes quoteEscapesInstance()
67+
{
68+
return sQuoteEscapesInstance;
69+
}
70+
71+
public static CsvCharacterEscapes controlEscapesInstance()
72+
{
73+
return sControlEscapesInstance;
74+
}
75+
76+
public static CsvCharacterEscapes controlQuoteEscapesInstance()
77+
{
78+
return sControlQuoteEscapesInstance;
79+
}
80+
81+
public static CsvCharacterEscapes fromCsvFeatures(int csvFeatures)
82+
{
83+
int idx = 0;
84+
idx |= CsvGenerator.Feature.ESCAPE_QUOTE_CHAR_WITH_ESCAPE_CHAR.enabledIn(csvFeatures) ? 1 : 0;
85+
idx |= Feature.ESCAPE_CONTROL_CHARS_WITH_ESCAPE_CHAR.enabledIn(csvFeatures) ? 2 : 0;
86+
87+
return sEscapes[idx];
88+
}
89+
90+
@Override
91+
public SerializableString getEscapeSequence(int ch)
92+
{
93+
return null; // unused for CSV escapes
94+
}
95+
96+
@Override
97+
public int[] getEscapeCodesForAscii()
98+
{
99+
return escapes;
100+
}
101+
}

csv/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ protected CsvGenerator _createGenerator(IOContext ctxt, Writer out) throws IOExc
419419
CsvGenerator gen = new CsvGenerator(ctxt, _generatorFeatures, _csvGeneratorFeatures,
420420
_objectCodec, out, _schema);
421421
// any other initializations? No?
422+
423+
gen.setCharacterEscapes(CsvCharacterEscapes.fromCsvFeatures(_csvGeneratorFeatures));
422424
return gen;
423425
}
424426

csv/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import com.fasterxml.jackson.core.*;
99
import com.fasterxml.jackson.core.base.GeneratorBase;
10+
import com.fasterxml.jackson.core.io.CharacterEscapes;
1011
import com.fasterxml.jackson.core.json.JsonWriteContext;
1112
import com.fasterxml.jackson.core.io.IOContext;
1213
import com.fasterxml.jackson.dataformat.csv.impl.CsvEncoder;
@@ -81,7 +82,19 @@ public enum Feature
8182
*
8283
* @since 2.9.3
8384
*/
84-
ESCAPE_QUOTE_CHAR_WITH_ESCAPE_CHAR(false)
85+
ESCAPE_QUOTE_CHAR_WITH_ESCAPE_CHAR(false),
86+
87+
/**
88+
* Feature that determines whether control characters (non-printable) are escaped using the
89+
* configured escape character. This feature allows LF and CR characters to be output as <pre>\n</pre>
90+
* and <pre>\r</pre> instead of being echoed out. This is a compatibility feature for some
91+
* parsers that can not read such output back in.
92+
* <p>
93+
* Default value is false so that control characters are echoed out (backwards compatible).
94+
*
95+
* @since 2.9.9
96+
*/
97+
ESCAPE_CONTROL_CHARS_WITH_ESCAPE_CHAR(false)
8598
;
8699

87100
protected final boolean _defaultState;
@@ -146,6 +159,8 @@ private Feature(boolean defaultState) {
146159
// note: can not be final since we may need to re-create it for new schema
147160
protected CsvEncoder _writer;
148161

162+
protected CharacterEscapes _characterEscapes = null;
163+
149164
/*
150165
/**********************************************************
151166
/* Output state
@@ -220,6 +235,8 @@ public CsvGenerator(IOContext ctxt, int jsonFeatures, int csvFeatures,
220235
_formatFeatures = csvFeatures;
221236
_schema = schema;
222237
_writer = new CsvEncoder(ctxt, csvFeatures, out, schema);
238+
239+
_writer.setOutputEscapes(CsvCharacterEscapes.fromCsvFeatures(csvFeatures).getEscapeCodesForAscii());
223240
}
224241

225242
public CsvGenerator(IOContext ctxt, int jsonFeatures, int csvFeatures,
@@ -312,6 +329,22 @@ public JsonGenerator overrideFormatFeatures(int values, int mask)
312329
return this;
313330
}
314331

332+
public JsonGenerator setCharacterEscapes(CharacterEscapes esc) {
333+
this._characterEscapes = esc;
334+
if (esc != null) {
335+
this._writer.setOutputEscapes(esc.getEscapeCodesForAscii());
336+
} else {
337+
this._writer.setOutputEscapes(CsvCharacterEscapes.fromCsvFeatures(_formatFeatures).getEscapeCodesForAscii());
338+
}
339+
340+
return this;
341+
}
342+
343+
public CharacterEscapes getCharacterEscapes() {
344+
return this._characterEscapes;
345+
}
346+
347+
315348
/*
316349
/**********************************************************
317350
/* Public API, capability introspection methods

0 commit comments

Comments
 (0)