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 public static final ContentType APPLICATION_ATOM_XML = create(
68 "application/atom+xml", StandardCharsets.UTF_8);
69 public static final ContentType APPLICATION_FORM_URLENCODED = create(
70 "application/x-www-form-urlencoded", StandardCharsets.ISO_8859_1);
71 public static final ContentType APPLICATION_JSON = create(
72 "application/json", StandardCharsets.UTF_8);
73 public static final ContentType APPLICATION_OCTET_STREAM = create(
74 "application/octet-stream", (Charset) null);
75 public static final ContentType APPLICATION_SOAP_XML = create(
76 "application/soap+xml", StandardCharsets.UTF_8);
77 public static final ContentType APPLICATION_SVG_XML = create(
78 "application/svg+xml", StandardCharsets.UTF_8);
79 public static final ContentType APPLICATION_XHTML_XML = create(
80 "application/xhtml+xml", StandardCharsets.UTF_8);
81 public static final ContentType APPLICATION_XML = create(
82 "application/xml", StandardCharsets.UTF_8);
83 public static final ContentType IMAGE_BMP = create(
84 "image/bmp");
85 public static final ContentType IMAGE_GIF = create(
86 "image/gif");
87 public static final ContentType IMAGE_JPEG = create(
88 "image/jpeg");
89 public static final ContentType IMAGE_PNG = create(
90 "image/png");
91 public static final ContentType IMAGE_SVG = create(
92 "image/svg+xml");
93 public static final ContentType IMAGE_TIFF = create(
94 "image/tiff");
95 public static final ContentType IMAGE_WEBP = create(
96 "image/webp");
97 public static final ContentType MULTIPART_FORM_DATA = create(
98 "multipart/form-data", StandardCharsets.ISO_8859_1);
99 public static final ContentType TEXT_HTML = create(
100 "text/html", StandardCharsets.ISO_8859_1);
101 public static final ContentType TEXT_PLAIN = create(
102 "text/plain", StandardCharsets.ISO_8859_1);
103 public static final ContentType TEXT_XML = create(
104 "text/xml", StandardCharsets.UTF_8);
105 public static final ContentType WILDCARD = create(
106 "*/*", (Charset) null);
107
108
109
110
111
112 @Deprecated
113 private static final Map<String, ContentType> CONTENT_TYPE_MAP;
114 static {
115
116 final ContentType[] contentTypes = {
117 APPLICATION_ATOM_XML,
118 APPLICATION_FORM_URLENCODED,
119 APPLICATION_JSON,
120 APPLICATION_SVG_XML,
121 APPLICATION_XHTML_XML,
122 APPLICATION_XML,
123 IMAGE_BMP,
124 IMAGE_GIF,
125 IMAGE_JPEG,
126 IMAGE_PNG,
127 IMAGE_SVG,
128 IMAGE_TIFF,
129 IMAGE_WEBP,
130 MULTIPART_FORM_DATA,
131 TEXT_HTML,
132 TEXT_PLAIN,
133 TEXT_XML };
134 final HashMap<String, ContentType> map = new HashMap<>();
135 for (final ContentType contentType: contentTypes) {
136 map.put(contentType.getMimeType(), contentType);
137 }
138 CONTENT_TYPE_MAP = Collections.unmodifiableMap(map);
139 }
140
141
142 public static final ContentType DEFAULT_TEXT = TEXT_PLAIN;
143 public static final ContentType DEFAULT_BINARY = APPLICATION_OCTET_STREAM;
144
145 private final String mimeType;
146 private final Charset charset;
147 private final NameValuePair[] params;
148
149 ContentType(
150 final String mimeType,
151 final Charset charset) {
152 this.mimeType = mimeType;
153 this.charset = charset;
154 this.params = null;
155 }
156
157 ContentType(
158 final String mimeType,
159 final Charset charset,
160 final NameValuePair[] params) {
161 this.mimeType = mimeType;
162 this.charset = charset;
163 this.params = params;
164 }
165
166 public String getMimeType() {
167 return this.mimeType;
168 }
169
170 public Charset getCharset() {
171 return this.charset;
172 }
173
174
175
176
177 public String getParameter(final String name) {
178 Args.notEmpty(name, "Parameter name");
179 if (this.params == null) {
180 return null;
181 }
182 for (final NameValuePair param: this.params) {
183 if (param.getName().equalsIgnoreCase(name)) {
184 return param.getValue();
185 }
186 }
187 return null;
188 }
189
190
191
192
193
194 @Override
195 public String toString() {
196 final CharArrayBufferrayBuffer.html#CharArrayBuffer">CharArrayBuffer buf = new CharArrayBuffer(64);
197 buf.append(this.mimeType);
198 if (this.params != null) {
199 buf.append("; ");
200 BasicHeaderValueFormatter.INSTANCE.formatParameters(buf, this.params, false);
201 } else if (this.charset != null) {
202 buf.append("; charset=");
203 buf.append(this.charset.name());
204 }
205 return buf.toString();
206 }
207
208 private static boolean valid(final String s) {
209 for (int i = 0; i < s.length(); i++) {
210 final char ch = s.charAt(i);
211 if (ch == '"' || ch == ',' || ch == ';') {
212 return false;
213 }
214 }
215 return true;
216 }
217
218
219
220
221
222
223
224
225
226 public static ContentType create(final String mimeType, final Charset charset) {
227 final String normalizedMimeType = Args.notBlank(mimeType, "MIME type").toLowerCase(Locale.ROOT);
228 Args.check(valid(normalizedMimeType), "MIME type may not contain reserved characters");
229 return new ContentType(normalizedMimeType, charset);
230 }
231
232
233
234
235
236
237
238
239 public static ContentType create(final String mimeType) {
240 return create(mimeType, (Charset) null);
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254 public static ContentType create(
255 final String mimeType, final String charset) throws UnsupportedCharsetException {
256 return create(mimeType, !TextUtils.isBlank(charset) ? Charset.forName(charset) : null);
257 }
258
259 private static ContentType create(final HeaderElement helem, final boolean strict) {
260 final String mimeType = helem.getName();
261 if (TextUtils.isBlank(mimeType)) {
262 return null;
263 }
264 return create(helem.getName(), helem.getParameters(), strict);
265 }
266
267 private static ContentType create(final String mimeType, final NameValuePair[] params, final boolean strict) {
268 Charset charset = null;
269 if (params != null) {
270 for (final NameValuePair param : params) {
271 if (param.getName().equalsIgnoreCase("charset")) {
272 final String s = param.getValue();
273 if (!TextUtils.isBlank(s)) {
274 try {
275 charset = Charset.forName(s);
276 } catch (final UnsupportedCharsetException ex) {
277 if (strict) {
278 throw ex;
279 }
280 }
281 }
282 break;
283 }
284 }
285 }
286 return new ContentType(mimeType, charset, params != null && params.length > 0 ? params : null);
287 }
288
289
290
291
292
293
294
295
296
297
298
299 public static ContentType create(
300 final String mimeType, final NameValuePair... params) throws UnsupportedCharsetException {
301 final String type = Args.notBlank(mimeType, "MIME type").toLowerCase(Locale.ROOT);
302 Args.check(valid(type), "MIME type may not contain reserved characters");
303 return create(mimeType, params, true);
304 }
305
306
307
308
309
310
311
312
313
314
315 public static ContentType parse(final CharSequence s) throws UnsupportedCharsetException {
316 return parse(s, true);
317 }
318
319
320
321
322
323
324
325
326
327
328 public static ContentType parseLenient(final CharSequence s) throws UnsupportedCharsetException {
329 return parse(s, false);
330 }
331
332 private static ContentType parse(final CharSequence s, final boolean strict) throws UnsupportedCharsetException {
333 if (TextUtils.isBlank(s)) {
334 return null;
335 }
336 final ParserCursore/ParserCursor.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, s.length());
337 final HeaderElement[] elements = BasicHeaderValueParser.INSTANCE.parseElements(s, cursor);
338 if (elements.length > 0) {
339 return create(elements[0], strict);
340 }
341 return null;
342 }
343
344
345
346
347
348
349
350
351
352
353
354 @Deprecated
355 public static ContentType getByMimeType(final String mimeType) {
356 if (mimeType == null) {
357 return null;
358 }
359 return CONTENT_TYPE_MAP.get(mimeType);
360 }
361
362
363
364
365
366
367
368
369 public ContentType withCharset(final Charset charset) {
370 return create(this.getMimeType(), charset);
371 }
372
373
374
375
376
377
378
379
380
381
382 public ContentType withCharset(final String charset) {
383 return create(this.getMimeType(), charset);
384 }
385
386
387
388
389
390
391
392
393 public ContentType withParameters(
394 final NameValuePair... params) throws UnsupportedCharsetException {
395 if (params.length == 0) {
396 return this;
397 }
398 final Map<String, String> paramMap = new LinkedHashMap<>();
399 if (this.params != null) {
400 for (final NameValuePair param: this.params) {
401 paramMap.put(param.getName(), param.getValue());
402 }
403 }
404 for (final NameValuePair param: params) {
405 paramMap.put(param.getName(), param.getValue());
406 }
407 final List<NameValuePair> newParams = new ArrayList<>(paramMap.size() + 1);
408 if (this.charset != null && !paramMap.containsKey("charset")) {
409 newParams.add(new BasicNameValuePair("charset", this.charset.name()));
410 }
411 for (final Map.Entry<String, String> entry: paramMap.entrySet()) {
412 newParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
413 }
414 return create(this.getMimeType(), newParams.toArray(new NameValuePair[newParams.size()]), true);
415 }
416
417 public boolean isSameMimeType(final ContentType contentType) {
418 return contentType != null && mimeType.equalsIgnoreCase(contentType.getMimeType());
419 }
420
421 }