1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.http;
29
30 import java.io.Serializable;
31 import java.nio.charset.Charset;
32 import java.nio.charset.StandardCharsets;
33 import java.nio.charset.UnsupportedCharsetException;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.LinkedHashMap;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.Map;
41
42 import org.apache.hc.core5.annotation.Contract;
43 import org.apache.hc.core5.annotation.ThreadingBehavior;
44 import org.apache.hc.core5.http.message.BasicHeaderValueFormatter;
45 import org.apache.hc.core5.http.message.BasicHeaderValueParser;
46 import org.apache.hc.core5.http.message.BasicNameValuePair;
47 import org.apache.hc.core5.http.message.ParserCursor;
48 import org.apache.hc.core5.util.Args;
49 import org.apache.hc.core5.util.CharArrayBuffer;
50 import org.apache.hc.core5.util.TextUtils;
51
52
53
54
55
56
57
58
59
60
61 @Contract(threading = ThreadingBehavior.IMMUTABLE)
62 public final class ContentType implements Serializable {
63
64 private static final long serialVersionUID = -7768694718232371896L;
65
66
67
68
69 private static final String CHARSET = "charset";
70
71
72 public static final ContentType APPLICATION_ATOM_XML = create(
73 "application/atom+xml", StandardCharsets.UTF_8);
74 public static final ContentType APPLICATION_FORM_URLENCODED = create(
75 "application/x-www-form-urlencoded", StandardCharsets.ISO_8859_1);
76 public static final ContentType APPLICATION_JSON = create(
77 "application/json", StandardCharsets.UTF_8);
78
79
80
81
82
83 public static final ContentType APPLICATION_NDJSON = create(
84 "application/x-ndjson", StandardCharsets.UTF_8);
85
86 public static final ContentType APPLICATION_OCTET_STREAM = create(
87 "application/octet-stream", (Charset) null);
88
89
90
91
92 public static final ContentType APPLICATION_PDF = create(
93 "application/pdf", StandardCharsets.UTF_8);
94
95 public static final ContentType APPLICATION_SOAP_XML = create(
96 "application/soap+xml", StandardCharsets.UTF_8);
97 public static final ContentType APPLICATION_SVG_XML = create(
98 "application/svg+xml", StandardCharsets.UTF_8);
99 public static final ContentType APPLICATION_XHTML_XML = create(
100 "application/xhtml+xml", StandardCharsets.UTF_8);
101 public static final ContentType APPLICATION_XML = create(
102 "application/xml", StandardCharsets.UTF_8);
103
104
105
106
107
108 public static final ContentType APPLICATION_PROBLEM_JSON = create(
109 "application/problem+json", StandardCharsets.UTF_8);
110
111
112
113
114
115 public static final ContentType APPLICATION_PROBLEM_XML = create(
116 "application/problem+xml", StandardCharsets.UTF_8);
117
118
119
120
121
122 public static final ContentType APPLICATION_RSS_XML = create(
123 "application/rss+xml", StandardCharsets.UTF_8);
124
125 public static final ContentType IMAGE_BMP = create(
126 "image/bmp");
127 public static final ContentType IMAGE_GIF = create(
128 "image/gif");
129 public static final ContentType IMAGE_JPEG = create(
130 "image/jpeg");
131 public static final ContentType IMAGE_PNG = create(
132 "image/png");
133 public static final ContentType IMAGE_SVG = create(
134 "image/svg+xml");
135 public static final ContentType IMAGE_TIFF = create(
136 "image/tiff");
137 public static final ContentType IMAGE_WEBP = create(
138 "image/webp");
139 public static final ContentType MULTIPART_FORM_DATA = create(
140 "multipart/form-data", StandardCharsets.ISO_8859_1);
141
142
143
144
145
146 public static final ContentType MULTIPART_MIXED = create(
147 "multipart/mixed", StandardCharsets.ISO_8859_1);
148
149
150
151
152
153 public static final ContentType MULTIPART_RELATED = create(
154 "multipart/related", StandardCharsets.ISO_8859_1);
155
156 public static final ContentType TEXT_HTML = create(
157 "text/html", StandardCharsets.ISO_8859_1);
158
159
160
161
162
163 public static final ContentType TEXT_MARKDOWN = create(
164 "text/markdown", StandardCharsets.UTF_8);
165
166 public static final ContentType TEXT_PLAIN = create(
167 "text/plain", StandardCharsets.ISO_8859_1);
168 public static final ContentType TEXT_XML = create(
169 "text/xml", StandardCharsets.UTF_8);
170
171
172
173
174
175 public static final ContentType TEXT_EVENT_STREAM = create(
176 "text/event-stream", StandardCharsets.UTF_8);
177
178 public static final ContentType WILDCARD = create(
179 "*/*", (Charset) null);
180
181
182
183
184 private static final NameValuePairir">NameValuePair[] EMPTY_NAME_VALUE_PAIR_ARRAY = new NameValuePair[0];
185
186
187
188
189 @Deprecated
190 private static final Map<String, ContentType> CONTENT_TYPE_MAP;
191 static {
192
193 final ContentType[] contentTypes = {
194 APPLICATION_ATOM_XML,
195 APPLICATION_FORM_URLENCODED,
196 APPLICATION_JSON,
197 APPLICATION_SVG_XML,
198 APPLICATION_XHTML_XML,
199 APPLICATION_XML,
200 IMAGE_BMP,
201 IMAGE_GIF,
202 IMAGE_JPEG,
203 IMAGE_PNG,
204 IMAGE_SVG,
205 IMAGE_TIFF,
206 IMAGE_WEBP,
207 MULTIPART_FORM_DATA,
208 TEXT_HTML,
209 TEXT_PLAIN,
210 TEXT_XML };
211 final HashMap<String, ContentType> map = new HashMap<>();
212 for (final ContentType contentType: contentTypes) {
213 map.put(contentType.getMimeType(), contentType);
214 }
215 CONTENT_TYPE_MAP = Collections.unmodifiableMap(map);
216 }
217
218
219 public static final ContentType DEFAULT_TEXT = TEXT_PLAIN;
220 public static final ContentType DEFAULT_BINARY = APPLICATION_OCTET_STREAM;
221
222 private final String mimeType;
223 private final Charset charset;
224 private final NameValuePair[] params;
225
226 ContentType(
227 final String mimeType,
228 final Charset charset) {
229 this.mimeType = mimeType;
230 this.charset = charset;
231 this.params = null;
232 }
233
234 ContentType(
235 final String mimeType,
236 final Charset charset,
237 final NameValuePair[] params) {
238 this.mimeType = mimeType;
239 this.charset = charset;
240 this.params = params;
241 }
242
243 public String getMimeType() {
244 return this.mimeType;
245 }
246
247 public Charset getCharset() {
248 return this.charset;
249 }
250
251
252
253
254 public String getParameter(final String name) {
255 Args.notEmpty(name, "Parameter name");
256 if (this.params == null) {
257 return null;
258 }
259 for (final NameValuePair param: this.params) {
260 if (param.getName().equalsIgnoreCase(name)) {
261 return param.getValue();
262 }
263 }
264 return null;
265 }
266
267
268
269
270
271 @Override
272 public String toString() {
273 final CharArrayBufferrayBuffer.html#CharArrayBuffer">CharArrayBuffer buf = new CharArrayBuffer(64);
274 buf.append(this.mimeType);
275 if (this.params != null) {
276 buf.append("; ");
277 BasicHeaderValueFormatter.INSTANCE.formatParameters(buf, this.params, false);
278 } else if (this.charset != null) {
279 buf.append("; charset=");
280 buf.append(this.charset.name());
281 }
282 return buf.toString();
283 }
284
285 private static boolean valid(final String s) {
286 for (int i = 0; i < s.length(); i++) {
287 final char ch = s.charAt(i);
288 if (ch == '"' || ch == ',' || ch == ';') {
289 return false;
290 }
291 }
292 return true;
293 }
294
295
296
297
298
299
300
301
302
303 public static ContentType create(final String mimeType, final Charset charset) {
304 final String normalizedMimeType = Args.notBlank(mimeType, "MIME type").toLowerCase(Locale.ROOT);
305 Args.check(valid(normalizedMimeType), "MIME type may not contain reserved characters");
306 return new ContentType(normalizedMimeType, charset);
307 }
308
309
310
311
312
313
314
315
316 public static ContentType create(final String mimeType) {
317 return create(mimeType, (Charset) null);
318 }
319
320
321
322
323
324
325
326
327
328
329
330
331 public static ContentType create(
332 final String mimeType, final String charset) throws UnsupportedCharsetException {
333 return create(mimeType, !TextUtils.isBlank(charset) ? Charset.forName(charset) : null);
334 }
335
336 private static ContentType create(final HeaderElement helem, final boolean strict) {
337 final String mimeType = helem.getName();
338 if (TextUtils.isBlank(mimeType)) {
339 return null;
340 }
341 return create(helem.getName(), helem.getParameters(), strict);
342 }
343
344 private static ContentType create(final String mimeType, final NameValuePair[] params, final boolean strict) {
345 Charset charset = null;
346 if (params != null) {
347 for (final NameValuePair param : params) {
348 if (param.getName().equalsIgnoreCase(CHARSET)) {
349 final String s = param.getValue();
350 if (!TextUtils.isBlank(s)) {
351 try {
352 charset = Charset.forName(s);
353 } catch (final UnsupportedCharsetException ex) {
354 if (strict) {
355 throw ex;
356 }
357 }
358 }
359 break;
360 }
361 }
362 }
363 return new ContentType(mimeType, charset, params != null && params.length > 0 ? params : null);
364 }
365
366
367
368
369
370
371
372
373
374
375
376 public static ContentType create(
377 final String mimeType, final NameValuePair... params) throws UnsupportedCharsetException {
378 final String type = Args.notBlank(mimeType, "MIME type").toLowerCase(Locale.ROOT);
379 Args.check(valid(type), "MIME type may not contain reserved characters");
380 return create(mimeType, params, true);
381 }
382
383
384
385
386
387
388
389
390
391
392 public static ContentType parse(final CharSequence s) throws UnsupportedCharsetException {
393 return parse(s, true);
394 }
395
396
397
398
399
400
401
402
403
404
405 public static ContentType parseLenient(final CharSequence s) throws UnsupportedCharsetException {
406 return parse(s, false);
407 }
408
409 private static ContentType parse(final CharSequence s, final boolean strict) throws UnsupportedCharsetException {
410 if (TextUtils.isBlank(s)) {
411 return null;
412 }
413 final ParserCursore/ParserCursor.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, s.length());
414 final HeaderElement[] elements = BasicHeaderValueParser.INSTANCE.parseElements(s, cursor);
415 if (elements.length > 0) {
416 return create(elements[0], strict);
417 }
418 return null;
419 }
420
421
422
423
424
425
426
427
428
429
430
431 @Deprecated
432 public static ContentType getByMimeType(final String mimeType) {
433 if (mimeType == null) {
434 return null;
435 }
436 return CONTENT_TYPE_MAP.get(mimeType);
437 }
438
439
440
441
442
443
444
445
446 public ContentType withCharset(final Charset charset) {
447 return create(this.getMimeType(), charset);
448 }
449
450
451
452
453
454
455
456
457
458
459 public ContentType withCharset(final String charset) {
460 return create(this.getMimeType(), charset);
461 }
462
463
464
465
466
467
468
469
470 public ContentType withParameters(
471 final NameValuePair... params) throws UnsupportedCharsetException {
472 if (params.length == 0) {
473 return this;
474 }
475 final Map<String, String> paramMap = new LinkedHashMap<>();
476 if (this.params != null) {
477 for (final NameValuePair param: this.params) {
478 paramMap.put(param.getName(), param.getValue());
479 }
480 }
481 for (final NameValuePair param: params) {
482 paramMap.put(param.getName(), param.getValue());
483 }
484 final List<NameValuePair> newParams = new ArrayList<>(paramMap.size() + 1);
485 if (this.charset != null && !paramMap.containsKey(CHARSET)) {
486 newParams.add(new BasicNameValuePair(CHARSET, this.charset.name()));
487 }
488 for (final Map.Entry<String, String> entry: paramMap.entrySet()) {
489 newParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
490 }
491 return create(this.getMimeType(), newParams.toArray(EMPTY_NAME_VALUE_PAIR_ARRAY), true);
492 }
493
494 public boolean isSameMimeType(final ContentType contentType) {
495 return contentType != null && mimeType.equalsIgnoreCase(contentType.getMimeType());
496 }
497
498 }