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