23
23
import java .util .Arrays ;
24
24
import java .util .List ;
25
25
import java .util .Map ;
26
+ import java .util .concurrent .ConcurrentHashMap ;
27
+ import java .util .concurrent .ConcurrentMap ;
26
28
import java .util .stream .Collectors ;
27
29
28
30
import org .reactivestreams .Publisher ;
35
37
import org .springframework .core .io .buffer .PooledDataBuffer ;
36
38
import org .springframework .core .log .LogFormatUtils ;
37
39
import org .springframework .lang .Nullable ;
40
+ import org .springframework .util .Assert ;
38
41
import org .springframework .util .MimeType ;
39
42
import org .springframework .util .MimeTypeUtils ;
40
43
@@ -63,20 +66,18 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
63
66
/** The default delimiter strings to use, i.e. {@code \r\n} and {@code \n}. */
64
67
public static final List <String > DEFAULT_DELIMITERS = Arrays .asList ("\r \n " , "\n " );
65
68
66
- private static final List <byte []> DEFAULT_DELIMITER_BYTES = DEFAULT_DELIMITERS .stream ()
67
- .map (str -> str .getBytes (StandardCharsets .UTF_8 ))
68
- .collect (Collectors .toList ());
69
69
70
-
71
- @ Nullable
72
70
private final List <String > delimiters ;
73
71
74
72
private final boolean stripDelimiter ;
75
73
74
+ private final ConcurrentMap <Charset , List <byte []>> delimitersCache = new ConcurrentHashMap <>();
75
+
76
76
77
- private StringDecoder (@ Nullable List <String > delimiters , boolean stripDelimiter , MimeType ... mimeTypes ) {
77
+ private StringDecoder (List <String > delimiters , boolean stripDelimiter , MimeType ... mimeTypes ) {
78
78
super (mimeTypes );
79
- this .delimiters = delimiters != null ? new ArrayList <>(delimiters ) : null ;
79
+ Assert .notEmpty (delimiters , "'delimiters' must not be empty" );
80
+ this .delimiters = new ArrayList <>(delimiters );
80
81
this .stripDelimiter = stripDelimiter ;
81
82
}
82
83
@@ -90,9 +91,7 @@ public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType
90
91
public Flux <String > decode (Publisher <DataBuffer > inputStream , ResolvableType elementType ,
91
92
@ Nullable MimeType mimeType , @ Nullable Map <String , Object > hints ) {
92
93
93
- List <byte []> delimiterBytes = this .delimiters != null ?
94
- this .delimiters .stream ().map (s -> s .getBytes (getCharset (mimeType ))).collect (Collectors .toList ()) :
95
- DEFAULT_DELIMITER_BYTES ;
94
+ List <byte []> delimiterBytes = getDelimiterBytes (mimeType );
96
95
97
96
Flux <DataBuffer > inputFlux = Flux .from (inputStream )
98
97
.flatMapIterable (dataBuffer -> splitOnDelimiter (dataBuffer , delimiterBytes ))
@@ -103,6 +102,13 @@ public Flux<String> decode(Publisher<DataBuffer> inputStream, ResolvableType ele
103
102
return super .decode (inputFlux , elementType , mimeType , hints );
104
103
}
105
104
105
+ private List <byte []> getDelimiterBytes (@ Nullable MimeType mimeType ) {
106
+ return this .delimitersCache .computeIfAbsent (getCharset (mimeType ),
107
+ charset -> this .delimiters .stream ()
108
+ .map (s -> s .getBytes (charset ))
109
+ .collect (Collectors .toList ()));
110
+ }
111
+
106
112
/**
107
113
* Splits the given data buffer on delimiter boundaries. The returned Flux contains a
108
114
* {@link #END_FRAME} buffer after each delimiter.
@@ -234,17 +240,16 @@ public static StringDecoder textPlainOnly(boolean ignored) {
234
240
* Create a {@code StringDecoder} for {@code "text/plain"}.
235
241
*/
236
242
public static StringDecoder textPlainOnly () {
237
- return textPlainOnly (null , true );
243
+ return textPlainOnly (DEFAULT_DELIMITERS , true );
238
244
}
239
245
240
246
/**
241
247
* Create a {@code StringDecoder} for {@code "text/plain"}.
242
- * @param delimiters delimiter strings to use to split the input stream, if
243
- * {@code null} by default {@link #DEFAULT_DELIMITERS} is used.
248
+ * @param delimiters delimiter strings to use to split the input stream
244
249
* @param stripDelimiter whether to remove delimiters from the resulting
245
250
* input strings.
246
251
*/
247
- public static StringDecoder textPlainOnly (@ Nullable List <String > delimiters , boolean stripDelimiter ) {
252
+ public static StringDecoder textPlainOnly (List <String > delimiters , boolean stripDelimiter ) {
248
253
return new StringDecoder (delimiters , stripDelimiter , new MimeType ("text" , "plain" , DEFAULT_CHARSET ));
249
254
}
250
255
@@ -263,17 +268,16 @@ public static StringDecoder allMimeTypes(boolean ignored) {
263
268
* Create a {@code StringDecoder} that supports all MIME types.
264
269
*/
265
270
public static StringDecoder allMimeTypes () {
266
- return allMimeTypes (null , true );
271
+ return allMimeTypes (DEFAULT_DELIMITERS , true );
267
272
}
268
273
269
274
/**
270
275
* Create a {@code StringDecoder} that supports all MIME types.
271
- * @param delimiters delimiter strings to use to split the input stream, if
272
- * {@code null} by default {@link #DEFAULT_DELIMITERS} is used.
276
+ * @param delimiters delimiter strings to use to split the input stream
273
277
* @param stripDelimiter whether to remove delimiters from the resulting
274
278
* input strings.
275
279
*/
276
- public static StringDecoder allMimeTypes (@ Nullable List <String > delimiters , boolean stripDelimiter ) {
280
+ public static StringDecoder allMimeTypes (List <String > delimiters , boolean stripDelimiter ) {
277
281
return new StringDecoder (delimiters , stripDelimiter ,
278
282
new MimeType ("text" , "plain" , DEFAULT_CHARSET ), MimeTypeUtils .ALL );
279
283
}
0 commit comments