001/*
002 * Copyright (C) 2011 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.net;
016
017import static com.google.common.base.CharMatcher.ascii;
018import static com.google.common.base.CharMatcher.javaIsoControl;
019import static com.google.common.base.MoreObjects.firstNonNull;
020import static com.google.common.base.Preconditions.checkArgument;
021import static com.google.common.base.Preconditions.checkNotNull;
022import static com.google.common.base.Preconditions.checkState;
023import static com.google.common.collect.Maps.transformValues;
024import static java.nio.charset.StandardCharsets.UTF_8;
025import static java.util.Objects.hash;
026
027import com.google.common.annotations.GwtCompatible;
028import com.google.common.base.Ascii;
029import com.google.common.base.CharMatcher;
030import com.google.common.base.Joiner;
031import com.google.common.base.Joiner.MapJoiner;
032import com.google.common.base.Optional;
033import com.google.common.collect.ImmutableListMultimap;
034import com.google.common.collect.ImmutableMultiset;
035import com.google.common.collect.ImmutableSet;
036import com.google.common.collect.Multimap;
037import com.google.common.collect.Multimaps;
038import com.google.errorprone.annotations.CanIgnoreReturnValue;
039import com.google.errorprone.annotations.Immutable;
040import com.google.errorprone.annotations.concurrent.LazyInit;
041import java.nio.charset.Charset;
042import java.nio.charset.IllegalCharsetNameException;
043import java.nio.charset.UnsupportedCharsetException;
044import java.util.HashMap;
045import java.util.Map;
046import java.util.Map.Entry;
047import org.jspecify.annotations.Nullable;
048
049/**
050 * Represents an <a href="http://en.wikipedia.org/wiki/Internet_media_type">Internet Media Type</a>
051 * (also known as a MIME Type or Content Type). This class also supports the concept of media ranges
052 * <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-14.1">defined by HTTP/1.1</a>. As
053 * such, the {@code *} character is treated as a wildcard and is used to represent any acceptable
054 * type or subtype value. A media type may not have wildcard type with a declared subtype. The
055 * {@code *} character has no special meaning as part of a parameter. All values for type, subtype,
056 * parameter attributes or parameter values must be valid according to RFCs <a
057 * href="https://tools.ietf.org/html/rfc2045">2045</a> and <a
058 * href="https://tools.ietf.org/html/rfc2046">2046</a>.
059 *
060 * <p>All portions of the media type that are case-insensitive (type, subtype, parameter attributes)
061 * are normalized to lowercase. The value of the {@code charset} parameter is normalized to
062 * lowercase, but all others are left as-is.
063 *
064 * <p>Note that this specifically does <strong>not</strong> represent the value of the MIME {@code
065 * Content-Type} header and as such has no support for header-specific considerations such as line
066 * folding and comments.
067 *
068 * <p>For media types that take a charset the predefined constants default to UTF-8 and have a
069 * "_UTF_8" suffix. To get a version without a character set, use {@link #withoutParameters}.
070 *
071 * @since 12.0
072 * @author Gregory Kick
073 */
074@GwtCompatible
075@Immutable
076public final class MediaType {
077  private static final String CHARSET_ATTRIBUTE = "charset";
078  private static final ImmutableListMultimap<String, String> UTF_8_CONSTANT_PARAMETERS =
079      ImmutableListMultimap.of(CHARSET_ATTRIBUTE, Ascii.toLowerCase(UTF_8.name()));
080
081  /** Matcher for type, subtype and attributes. */
082  private static final CharMatcher TOKEN_MATCHER =
083      ascii()
084          .and(javaIsoControl().negate())
085          .and(CharMatcher.isNot(' '))
086          .and(CharMatcher.noneOf("()<>@,;:\\\"/[]?="));
087
088  private static final CharMatcher QUOTED_TEXT_MATCHER = ascii().and(CharMatcher.noneOf("\"\\\r"));
089
090  /*
091   * This matches the same characters as linear-white-space from RFC 822, but we make no effort to
092   * enforce any particular rules with regards to line folding as stated in the class docs.
093   */
094  private static final CharMatcher LINEAR_WHITE_SPACE = CharMatcher.anyOf(" \t\r\n");
095
096  // TODO(gak): make these public?
097  private static final String APPLICATION_TYPE = "application";
098  private static final String AUDIO_TYPE = "audio";
099  private static final String IMAGE_TYPE = "image";
100  private static final String TEXT_TYPE = "text";
101  private static final String VIDEO_TYPE = "video";
102  private static final String FONT_TYPE = "font";
103
104  private static final String WILDCARD = "*";
105
106  private static final Map<MediaType, MediaType> knownTypes = new HashMap<>();
107
108  private static MediaType createConstant(String type, String subtype) {
109    MediaType mediaType = addKnownType(new MediaType(type, subtype, ImmutableListMultimap.of()));
110    mediaType.parsedCharset = Optional.absent();
111    return mediaType;
112  }
113
114  private static MediaType createConstantUtf8(String type, String subtype) {
115    MediaType mediaType = addKnownType(new MediaType(type, subtype, UTF_8_CONSTANT_PARAMETERS));
116    mediaType.parsedCharset = Optional.of(UTF_8);
117    return mediaType;
118  }
119
120  @CanIgnoreReturnValue
121  private static MediaType addKnownType(MediaType mediaType) {
122    knownTypes.put(mediaType, mediaType);
123    return mediaType;
124  }
125
126  /*
127   * The following constants are grouped by their type and ordered alphabetically by the constant
128   * name within that type. The constant name should be a sensible identifier that is closest to the
129   * "common name" of the media. This is often, but not necessarily the same as the subtype.
130   *
131   * Be sure to declare all constants with the type and subtype in all lowercase. For types that
132   * take a charset (e.g. all text/* types), default to UTF-8 and suffix the constant name with
133   * "_UTF_8".
134   */
135
136  /** Wildcard matching any media type. */
137  public static final MediaType ANY_TYPE = createConstant(WILDCARD, WILDCARD);
138
139  /** Wildcard matching any "text" top-level media type. */
140  public static final MediaType ANY_TEXT_TYPE = createConstant(TEXT_TYPE, WILDCARD);
141
142  /** Wildcard matching any "image" top-level media type. */
143  public static final MediaType ANY_IMAGE_TYPE = createConstant(IMAGE_TYPE, WILDCARD);
144
145  /** Wildcard matching any "audio" top-level media type. */
146  public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD);
147
148  /** Wildcard matching any "video" top-level media type. */
149  public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD);
150
151  /** Wildcard matching any "application" top-level media type. */
152  public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD);
153
154  /**
155   * Wildcard matching any "font" top-level media type.
156   *
157   * @since 30.0
158   */
159  public static final MediaType ANY_FONT_TYPE = createConstant(FONT_TYPE, WILDCARD);
160
161  /* text types */
162  /**
163   * <a
164   * href="https://web.archive.org/web/20190215171256/https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache#The_cache_manifest_file">Cache
165   * Manifest</a>.
166   */
167  public static final MediaType CACHE_MANIFEST_UTF_8 =
168      createConstantUtf8(TEXT_TYPE, "cache-manifest");
169
170  /** <a href="https://www.rfc-editor.org/rfc/rfc2318.html">CSS</a>. */
171  public static final MediaType CSS_UTF_8 = createConstantUtf8(TEXT_TYPE, "css");
172
173  /** <a href="https://www.rfc-editor.org/rfc/rfc4180.html">CSV</a>. */
174  public static final MediaType CSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "csv");
175
176  /** <a href="https://www.rfc-editor.org/rfc/rfc2854.html">HTML</a>. */
177  public static final MediaType HTML_UTF_8 = createConstantUtf8(TEXT_TYPE, "html");
178
179  /** <a href="https://www.rfc-editor.org/rfc/rfc5545.html">iCalendar</a>. */
180  public static final MediaType I_CALENDAR_UTF_8 = createConstantUtf8(TEXT_TYPE, "calendar");
181
182  /**
183   * As described in <a href="https://www.rfc-editor.org/rfc/rfc7763.html">RFC 7763</a>, this
184   * constant ({@code text/markdown}) is used for Markdown documents.
185   *
186   * @since 33.3.0
187   */
188  public static final MediaType MD_UTF_8 = createConstantUtf8(TEXT_TYPE, "markdown");
189
190  /** <a href="https://www.rfc-editor.org/rfc/rfc2046.html#section-4.1">Plain text</a>. */
191  public static final MediaType PLAIN_TEXT_UTF_8 = createConstantUtf8(TEXT_TYPE, "plain");
192
193  /**
194   * <a href="http://www.rfc-editor.org/rfc/rfc4329.txt">RFC 4329</a> declares {@link
195   * #JAVASCRIPT_UTF_8 application/javascript} to be the correct media type for JavaScript, but this
196   * may be necessary in certain situations for compatibility.
197   */
198  public static final MediaType TEXT_JAVASCRIPT_UTF_8 = createConstantUtf8(TEXT_TYPE, "javascript");
199
200  /**
201   * <a href="http://www.iana.org/assignments/media-types/text/tab-separated-values">Tab separated
202   * values</a>.
203   *
204   * @since 15.0
205   */
206  public static final MediaType TSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "tab-separated-values");
207
208  /** <a href="https://www.rfc-editor.org/rfc/rfc6350.html">vCard</a>. */
209  public static final MediaType VCARD_UTF_8 = createConstantUtf8(TEXT_TYPE, "vcard");
210
211  /**
212   * UTF-8 encoded <a href="https://en.wikipedia.org/wiki/Wireless_Markup_Language">Wireless Markup
213   * Language</a>.
214   *
215   * @since 13.0
216   */
217  public static final MediaType WML_UTF_8 = createConstantUtf8(TEXT_TYPE, "vnd.wap.wml");
218
219  /**
220   * As described in <a href="http://www.ietf.org/rfc/rfc3023.txt">RFC 3023</a>, this constant
221   * ({@code text/xml}) is used for XML documents that are "readable by casual users." {@link
222   * #APPLICATION_XML_UTF_8} is provided for documents that are intended for applications.
223   */
224  public static final MediaType XML_UTF_8 = createConstantUtf8(TEXT_TYPE, "xml");
225
226  /**
227   * As described in <a href="https://w3c.github.io/webvtt/#iana-text-vtt">the VTT spec</a>, this is
228   * used for Web Video Text Tracks (WebVTT) files, used with the HTML5 track element.
229   *
230   * @since 20.0
231   */
232  public static final MediaType VTT_UTF_8 = createConstantUtf8(TEXT_TYPE, "vtt");
233
234  /* image types */
235  /**
236   * <a href="https://en.wikipedia.org/wiki/BMP_file_format">Bitmap file format</a> ({@code bmp}
237   * files).
238   *
239   * @since 13.0
240   */
241  public static final MediaType BMP = createConstant(IMAGE_TYPE, "bmp");
242
243  /**
244   * The <a href="https://en.wikipedia.org/wiki/Camera_Image_File_Format">Canon Image File
245   * Format</a> ({@code crw} files), a widely-used "raw image" format for cameras. It is found in
246   * {@code /etc/mime.types}, e.g. in <a href=
247   * "http://anonscm.debian.org/gitweb/?p=collab-maint/mime-support.git;a=blob;f=mime.types;hb=HEAD"
248   * >Debian 3.48-1</a>.
249   *
250   * @since 15.0
251   */
252  public static final MediaType CRW = createConstant(IMAGE_TYPE, "x-canon-crw");
253
254  /**
255   * <a
256   * href="https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types#gif_graphics_interchange_format">GIF</a>.
257   */
258  public static final MediaType GIF = createConstant(IMAGE_TYPE, "gif");
259
260  /** <a href="https://www.iana.org/assignments/media-types/image/vnd.microsoft.icon">ICO</a>. */
261  public static final MediaType ICO = createConstant(IMAGE_TYPE, "vnd.microsoft.icon");
262
263  /** <a href="https://www.rfc-editor.org/rfc/rfc2046.html#section-4.2">JPEG</a>. */
264  public static final MediaType JPEG = createConstant(IMAGE_TYPE, "jpeg");
265
266  /** <a href="https://www.rfc-editor.org/rfc/rfc2083.html">PNG</a>. */
267  public static final MediaType PNG = createConstant(IMAGE_TYPE, "png");
268
269  /**
270   * The Photoshop File Format ({@code psd} files) as defined by <a
271   * href="http://www.iana.org/assignments/media-types/image/vnd.adobe.photoshop">IANA</a>, and
272   * found in {@code /etc/mime.types}, e.g. <a
273   * href="http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types"></a> of the
274   * Apache <a href="http://httpd.apache.org/">HTTPD project</a>; for the specification, see <a
275   * href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm">
276   * Adobe Photoshop Document Format</a> and <a
277   * href="http://en.wikipedia.org/wiki/Adobe_Photoshop#File_format">Wikipedia</a>; this is the
278   * regular output/input of Photoshop (which can also export to various image formats; note that
279   * files with extension "PSB" are in a distinct but related format).
280   *
281   * <p>This is a more recent replacement for the older, experimental type {@code x-photoshop}: <a
282   * href="http://tools.ietf.org/html/rfc2046#section-6">RFC-2046.6</a>.
283   *
284   * @since 15.0
285   */
286  public static final MediaType PSD = createConstant(IMAGE_TYPE, "vnd.adobe.photoshop");
287
288  /** <a href="https://www.w3.org/TR/SVG11/mimereg.html">SVG</a>. */
289  public static final MediaType SVG_UTF_8 = createConstantUtf8(IMAGE_TYPE, "svg+xml");
290
291  /** <a href="https://www.rfc-editor.org/rfc/rfc3302.html">TIFF</a>. */
292  public static final MediaType TIFF = createConstant(IMAGE_TYPE, "tiff");
293
294  /**
295   * <a href="https://en.wikipedia.org/wiki/AVIF">AVIF image format</a>.
296   *
297   * @since 33.5.0
298   */
299  public static final MediaType AVIF = createConstant(IMAGE_TYPE, "avif");
300
301  /**
302   * <a href="https://en.wikipedia.org/wiki/WebP">WebP image format</a>.
303   *
304   * @since 13.0
305   */
306  public static final MediaType WEBP = createConstant(IMAGE_TYPE, "webp");
307
308  /**
309   * <a href="https://www.iana.org/assignments/media-types/image/heif">HEIF image format</a>.
310   *
311   * @since 28.1
312   */
313  public static final MediaType HEIF = createConstant(IMAGE_TYPE, "heif");
314
315  /**
316   * <a href="https://tools.ietf.org/html/rfc3745">JP2K image format</a>.
317   *
318   * @since 28.1
319   */
320  public static final MediaType JP2K = createConstant(IMAGE_TYPE, "jp2");
321
322  /* audio types */
323  /** <a href="https://www.rfc-editor.org/rfc/rfc4337.html">MP4 audio</a>. */
324  public static final MediaType MP4_AUDIO = createConstant(AUDIO_TYPE, "mp4");
325
326  /** <a href="https://www.rfc-editor.org/rfc/rfc3003.html">MPEG audio</a>. */
327  public static final MediaType MPEG_AUDIO = createConstant(AUDIO_TYPE, "mpeg");
328
329  /** <a href="https://www.rfc-editor.org/rfc/rfc5334.html">Ogg audio</a>. */
330  public static final MediaType OGG_AUDIO = createConstant(AUDIO_TYPE, "ogg");
331
332  /** <a href="https://www.webmproject.org/docs/container/">WebM audio</a>. */
333  public static final MediaType WEBM_AUDIO = createConstant(AUDIO_TYPE, "webm");
334
335  /**
336   * L16 audio, as defined by <a href="https://tools.ietf.org/html/rfc2586">RFC 2586</a>.
337   *
338   * @since 24.1
339   */
340  public static final MediaType L16_AUDIO = createConstant(AUDIO_TYPE, "l16");
341
342  /**
343   * L24 audio, as defined by <a href="https://tools.ietf.org/html/rfc3190">RFC 3190</a>.
344   *
345   * @since 20.0
346   */
347  public static final MediaType L24_AUDIO = createConstant(AUDIO_TYPE, "l24");
348
349  /**
350   * Basic Audio, as defined by <a href="http://tools.ietf.org/html/rfc2046#section-4.3">RFC
351   * 2046</a>.
352   *
353   * @since 20.0
354   */
355  public static final MediaType BASIC_AUDIO = createConstant(AUDIO_TYPE, "basic");
356
357  /**
358   * Advanced Audio Coding. For more information, see <a
359   * href="https://en.wikipedia.org/wiki/Advanced_Audio_Coding">Advanced Audio Coding</a>.
360   *
361   * @since 20.0
362   */
363  public static final MediaType AAC_AUDIO = createConstant(AUDIO_TYPE, "aac");
364
365  /**
366   * Vorbis Audio, as defined by <a href="http://tools.ietf.org/html/rfc5215">RFC 5215</a>.
367   *
368   * @since 20.0
369   */
370  public static final MediaType VORBIS_AUDIO = createConstant(AUDIO_TYPE, "vorbis");
371
372  /**
373   * Windows Media Audio. For more information, see <a
374   * href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd562994(v=vs.85).aspx">file
375   * name extensions for Windows Media metafiles</a>.
376   *
377   * @since 20.0
378   */
379  public static final MediaType WMA_AUDIO = createConstant(AUDIO_TYPE, "x-ms-wma");
380
381  /**
382   * Windows Media metafiles. For more information, see <a
383   * href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd562994(v=vs.85).aspx">file
384   * name extensions for Windows Media metafiles</a>.
385   *
386   * @since 20.0
387   */
388  public static final MediaType WAX_AUDIO = createConstant(AUDIO_TYPE, "x-ms-wax");
389
390  /**
391   * Real Audio. For more information, see <a
392   * href="http://service.real.com/help/faq/rp8/configrp8win.html">this link</a>.
393   *
394   * @since 20.0
395   */
396  public static final MediaType VND_REAL_AUDIO = createConstant(AUDIO_TYPE, "vnd.rn-realaudio");
397
398  /**
399   * WAVE format, as defined by <a href="https://tools.ietf.org/html/rfc2361">RFC 2361</a>.
400   *
401   * @since 20.0
402   */
403  public static final MediaType VND_WAVE_AUDIO = createConstant(AUDIO_TYPE, "vnd.wave");
404
405  /* video types */
406  /** <a href="https://www.rfc-editor.org/rfc/rfc4337.html">MP4 video</a>. */
407  public static final MediaType MP4_VIDEO = createConstant(VIDEO_TYPE, "mp4");
408
409  /** <a href="https://www.rfc-editor.org/rfc/rfc2046.html#section-4.4">MPEG video</a>. */
410  public static final MediaType MPEG_VIDEO = createConstant(VIDEO_TYPE, "mpeg");
411
412  /** <a href="https://www.rfc-editor.org/rfc/rfc5334.html">Ogg video</a>. */
413  public static final MediaType OGG_VIDEO = createConstant(VIDEO_TYPE, "ogg");
414
415  /** <a href="https://www.iana.org/assignments/media-types/video/quicktime">QuickTime video</a>. */
416  public static final MediaType QUICKTIME = createConstant(VIDEO_TYPE, "quicktime");
417
418  /** <a href="https://www.webmproject.org/docs/container/">WebM video</a>. */
419  public static final MediaType WEBM_VIDEO = createConstant(VIDEO_TYPE, "webm");
420
421  /**
422   * <a
423   * href="https://learn.microsoft.com/en-us/previous-versions/windows/desktop/wmp/file-name-extensions">Windows
424   * Media Video</a>.
425   */
426  public static final MediaType WMV = createConstant(VIDEO_TYPE, "x-ms-wmv");
427
428  /**
429   * Flash video. For more information, see <a href=
430   * "http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7d48.html"
431   * >this link</a>.
432   *
433   * @since 20.0
434   */
435  public static final MediaType FLV_VIDEO = createConstant(VIDEO_TYPE, "x-flv");
436
437  /**
438   * The 3GP multimedia container format. For more information, see <a
439   * href="ftp://www.3gpp.org/tsg_sa/TSG_SA/TSGS_23/Docs/PDF/SP-040065.pdf#page=10">3GPP TS
440   * 26.244</a>.
441   *
442   * @since 20.0
443   */
444  public static final MediaType THREE_GPP_VIDEO = createConstant(VIDEO_TYPE, "3gpp");
445
446  /**
447   * The 3G2 multimedia container format. For more information, see <a
448   * href="http://www.3gpp2.org/Public_html/specs/C.S0050-B_v1.0_070521.pdf#page=16">3GPP2
449   * C.S0050-B</a>.
450   *
451   * @since 20.0
452   */
453  public static final MediaType THREE_GPP2_VIDEO = createConstant(VIDEO_TYPE, "3gpp2");
454
455  /* application types */
456  /**
457   * As described in <a href="http://www.ietf.org/rfc/rfc3023.txt">RFC 3023</a>, this constant
458   * ({@code application/xml}) is used for XML documents that are "unreadable by casual users."
459   * {@link #XML_UTF_8} is provided for documents that may be read by users.
460   *
461   * @since 14.0
462   */
463  public static final MediaType APPLICATION_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xml");
464
465  /** <a href="https://www.rfc-editor.org/rfc/rfc4287.html">Atom feeds</a>. */
466  public static final MediaType ATOM_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "atom+xml");
467
468  /** bzip2 archives. */
469  public static final MediaType BZIP2 = createConstant(APPLICATION_TYPE, "x-bzip2");
470
471  /**
472   * Files in the <a href="https://www.dartlang.org/articles/embedding-in-html/">dart</a>
473   * programming language.
474   *
475   * @since 19.0
476   */
477  public static final MediaType DART_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "dart");
478
479  /**
480   * <a
481   * href="https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/DistributingPasses.html">Apple
482   * Passbook</a>.
483   *
484   * @since 19.0
485   */
486  public static final MediaType APPLE_PASSBOOK =
487      createConstant(APPLICATION_TYPE, "vnd.apple.pkpass");
488
489  /**
490   * <a href="http://en.wikipedia.org/wiki/Embedded_OpenType">Embedded OpenType</a> fonts. This is
491   * <a href="http://www.iana.org/assignments/media-types/application/vnd.ms-fontobject">registered
492   * </a> with the IANA.
493   *
494   * @since 17.0
495   */
496  public static final MediaType EOT = createConstant(APPLICATION_TYPE, "vnd.ms-fontobject");
497
498  /**
499   * As described in the <a href="http://idpf.org/epub">International Digital Publishing Forum</a>
500   * EPUB is the distribution and interchange format standard for digital publications and
501   * documents. This media type is defined in the <a
502   * href="http://www.idpf.org/epub/30/spec/epub30-ocf.html">EPUB Open Container Format</a>
503   * specification.
504   *
505   * @since 15.0
506   */
507  public static final MediaType EPUB = createConstant(APPLICATION_TYPE, "epub+zip");
508
509  /** <a href="https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1">HTML form data</a>. */
510  public static final MediaType FORM_DATA =
511      createConstant(APPLICATION_TYPE, "x-www-form-urlencoded");
512
513  /**
514   * As described in <a href="https://www.rsa.com/rsalabs/node.asp?id=2138">PKCS #12: Personal
515   * Information Exchange Syntax Standard</a>, PKCS #12 defines an archive file format for storing
516   * many cryptography objects as a single file.
517   *
518   * @since 15.0
519   */
520  public static final MediaType KEY_ARCHIVE = createConstant(APPLICATION_TYPE, "pkcs12");
521
522  /**
523   * This is a non-standard media type, but is commonly used in serving hosted binary files as it is
524   * <a href="http://code.google.com/p/browsersec/wiki/Part2#Survey_of_content_sniffing_behaviors">
525   * known not to trigger content sniffing in current browsers</a>. It <i>should not</i> be used in
526   * other situations as it is not specified by any RFC and does not appear in the <a
527   * href="http://www.iana.org/assignments/media-types">/IANA MIME Media Types</a> list. Consider
528   * {@link #OCTET_STREAM} for binary data that is not being served to a browser.
529   *
530   * @since 14.0
531   */
532  public static final MediaType APPLICATION_BINARY = createConstant(APPLICATION_TYPE, "binary");
533
534  /**
535   * As described in <a href="https://www.rfc-editor.org/rfc/rfc8949.html">RFC 8949</a>, this
536   * constant ({@code application/cbor}) is used for the Concise Binary Object Representation (CBOR)
537   * data format.
538   *
539   * @since 33.4.0
540   */
541  public static final MediaType CBOR = createConstant(APPLICATION_TYPE, "cbor");
542
543  /**
544   * Media type for the <a href="https://tools.ietf.org/html/rfc7946">GeoJSON Format</a>, a
545   * geospatial data interchange format based on JSON.
546   *
547   * @since 28.0
548   */
549  public static final MediaType GEO_JSON = createConstant(APPLICATION_TYPE, "geo+json");
550
551  /** Gzip-compressed data. */
552  public static final MediaType GZIP = createConstant(APPLICATION_TYPE, "x-gzip");
553
554  /**
555   * <a href="https://tools.ietf.org/html/draft-kelly-json-hal-08#section-3">JSON Hypertext
556   * Application Language (HAL) documents</a>.
557   *
558   * @since 26.0
559   */
560  public static final MediaType HAL_JSON = createConstant(APPLICATION_TYPE, "hal+json");
561
562  /**
563   * <a href="http://www.rfc-editor.org/rfc/rfc4329.txt">RFC 4329</a> declares this to be the
564   * correct media type for JavaScript, but {@link #TEXT_JAVASCRIPT_UTF_8 text/javascript} may be
565   * necessary in certain situations for compatibility.
566   */
567  public static final MediaType JAVASCRIPT_UTF_8 =
568      createConstantUtf8(APPLICATION_TYPE, "javascript");
569
570  /**
571   * For <a href="https://tools.ietf.org/html/rfc7515">JWS or JWE objects using the Compact
572   * Serialization</a>.
573   *
574   * @since 27.1
575   */
576  public static final MediaType JOSE = createConstant(APPLICATION_TYPE, "jose");
577
578  /**
579   * For <a href="https://tools.ietf.org/html/rfc7515">JWS or JWE objects using the JSON
580   * Serialization</a>.
581   *
582   * @since 27.1
583   */
584  public static final MediaType JOSE_JSON = createConstant(APPLICATION_TYPE, "jose+json");
585
586  /** <a href="https://www.rfc-editor.org/rfc/rfc8259.html">JSON</a>. */
587  public static final MediaType JSON_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "json");
588
589  /**
590   * For <a href="https://tools.ietf.org/html/7519">JWT objects using the compact Serialization</a>.
591   *
592   * @since 32.0.0
593   */
594  public static final MediaType JWT = createConstant(APPLICATION_TYPE, "jwt");
595
596  /**
597   * The <a href="https://www.w3.org/TR/appmanifest/">Manifest for a web application</a>.
598   *
599   * @since 19.0
600   */
601  public static final MediaType MANIFEST_JSON_UTF_8 =
602      createConstantUtf8(APPLICATION_TYPE, "manifest+json");
603
604  /**
605   * <a href="http://www.opengeospatial.org/standards/kml/">OGC KML (Keyhole Markup Language)</a>.
606   */
607  public static final MediaType KML = createConstant(APPLICATION_TYPE, "vnd.google-earth.kml+xml");
608
609  /**
610   * <a href="http://www.opengeospatial.org/standards/kml/">OGC KML (Keyhole Markup Language)</a>,
611   * compressed using the ZIP format into KMZ archives.
612   */
613  public static final MediaType KMZ = createConstant(APPLICATION_TYPE, "vnd.google-earth.kmz");
614
615  /**
616   * The <a href="https://tools.ietf.org/html/rfc4155">mbox database format</a>.
617   *
618   * @since 13.0
619   */
620  public static final MediaType MBOX = createConstant(APPLICATION_TYPE, "mbox");
621
622  /**
623   * <a
624   * href="https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/profile-service/profile-service.html">Apple
625   * over-the-air mobile configuration profiles</a>.
626   *
627   * @since 18.0
628   */
629  public static final MediaType APPLE_MOBILE_CONFIG =
630      createConstant(APPLICATION_TYPE, "x-apple-aspen-config");
631
632  /**
633   * <a
634   * href="https://learn.microsoft.com/en-us/archive/blogs/vsofficedeveloper/office-2007-file-format-mime-types-for-http-content-streaming-2">Microsoft
635   * Excel</a> spreadsheets.
636   */
637  public static final MediaType MICROSOFT_EXCEL = createConstant(APPLICATION_TYPE, "vnd.ms-excel");
638
639  /**
640   * <a href="https://www.loc.gov/preservation/digital/formats/fdd/fdd000379.shtml">Microsoft
641   * Outlook</a> items.
642   *
643   * @since 27.1
644   */
645  public static final MediaType MICROSOFT_OUTLOOK =
646      createConstant(APPLICATION_TYPE, "vnd.ms-outlook");
647
648  /**
649   * <a
650   * href="https://learn.microsoft.com/en-us/archive/blogs/vsofficedeveloper/office-2007-file-format-mime-types-for-http-content-streaming-2">Microsoft
651   * Powerpoint</a> presentations.
652   */
653  public static final MediaType MICROSOFT_POWERPOINT =
654      createConstant(APPLICATION_TYPE, "vnd.ms-powerpoint");
655
656  /**
657   * <a
658   * href="https://learn.microsoft.com/en-us/archive/blogs/vsofficedeveloper/office-2007-file-format-mime-types-for-http-content-streaming-2">Microsoft
659   * Word</a> documents.
660   */
661  public static final MediaType MICROSOFT_WORD = createConstant(APPLICATION_TYPE, "msword");
662
663  /**
664   * Media type for <a
665   * href="https://en.wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP">Dynamic Adaptive
666   * Streaming over HTTP (DASH)</a>. This is <a
667   * href="https://www.iana.org/assignments/media-types/application/dash+xml">registered</a> with
668   * the IANA.
669   *
670   * @since 28.2
671   */
672  public static final MediaType MEDIA_PRESENTATION_DESCRIPTION =
673      createConstant(APPLICATION_TYPE, "dash+xml");
674
675  /**
676   * WASM applications. For more information see <a href="https://webassembly.org/">the Web Assembly
677   * overview</a>.
678   *
679   * @since 27.0
680   */
681  public static final MediaType WASM_APPLICATION = createConstant(APPLICATION_TYPE, "wasm");
682
683  /**
684   * NaCl applications. For more information see <a
685   * href="https://developer.chrome.com/native-client/devguide/coding/application-structure">the
686   * Developer Guide for Native Client Application Structure</a>.
687   *
688   * @since 20.0
689   */
690  public static final MediaType NACL_APPLICATION = createConstant(APPLICATION_TYPE, "x-nacl");
691
692  /**
693   * The <a href="https://pki-tutorial.readthedocs.io/en/latest/mime.html">PEM format</a>. See also
694   * <a href="https://github.com/ietf-wg-acme/acme/issues/120">ietf-wg-acme proposal</a>.
695   *
696   * @since 33.7.0
697   */
698  public static final MediaType PEM = createConstant(APPLICATION_TYPE, "x-pem-file");
699
700  /**
701   * NaCl portable applications. For more information see <a
702   * href="https://developer.chrome.com/native-client/devguide/coding/application-structure">the
703   * Developer Guide for Native Client Application Structure</a>.
704   *
705   * @since 20.0
706   */
707  public static final MediaType NACL_PORTABLE_APPLICATION =
708      createConstant(APPLICATION_TYPE, "x-pnacl");
709
710  /** <a href="https://www.rfc-editor.org/rfc/rfc2046.html#section-4.5.1">Binary data</a>. */
711  public static final MediaType OCTET_STREAM = createConstant(APPLICATION_TYPE, "octet-stream");
712
713  /** <a href="https://www.rfc-editor.org/rfc/rfc5334.html">Ogg container</a>. */
714  public static final MediaType OGG_CONTAINER = createConstant(APPLICATION_TYPE, "ogg");
715
716  /**
717   * <a
718   * href="https://www.iana.org/assignments/media-types/application/vnd.openxmlformats-officedocument.wordprocessingml.document">OOXML
719   * Document</a>.
720   */
721  public static final MediaType OOXML_DOCUMENT =
722      createConstant(
723          APPLICATION_TYPE, "vnd.openxmlformats-officedocument.wordprocessingml.document");
724
725  /**
726   * <a
727   * href="https://www.iana.org/assignments/media-types/application/vnd.openxmlformats-officedocument.presentationml.presentation">OOXML
728   * Presentation</a>.
729   */
730  public static final MediaType OOXML_PRESENTATION =
731      createConstant(
732          APPLICATION_TYPE, "vnd.openxmlformats-officedocument.presentationml.presentation");
733
734  /**
735   * <a
736   * href="https://www.iana.org/assignments/media-types/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">OOXML
737   * Sheet</a>.
738   */
739  public static final MediaType OOXML_SHEET =
740      createConstant(APPLICATION_TYPE, "vnd.openxmlformats-officedocument.spreadsheetml.sheet");
741
742  /**
743   * <a
744   * href="https://www.iana.org/assignments/media-types/application/vnd.oasis.opendocument.graphics">OpenDocument
745   * Graphics</a>.
746   */
747  public static final MediaType OPENDOCUMENT_GRAPHICS =
748      createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.graphics");
749
750  /**
751   * <a
752   * href="https://www.iana.org/assignments/media-types/application/vnd.oasis.opendocument.presentation">OpenDocument
753   * Presentation</a>.
754   */
755  public static final MediaType OPENDOCUMENT_PRESENTATION =
756      createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.presentation");
757
758  /**
759   * <a
760   * href="https://www.iana.org/assignments/media-types/application/vnd.oasis.opendocument.spreadsheet">OpenDocument
761   * Spreadsheet</a>.
762   */
763  public static final MediaType OPENDOCUMENT_SPREADSHEET =
764      createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.spreadsheet");
765
766  /**
767   * <a
768   * href="https://www.iana.org/assignments/media-types/application/vnd.oasis.opendocument.text">OpenDocument
769   * Text</a>.
770   */
771  public static final MediaType OPENDOCUMENT_TEXT =
772      createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.text");
773
774  /**
775   * <a href="https://tools.ietf.org/id/draft-ellermann-opensearch-01.html">OpenSearch</a>
776   * Description files are XML files that describe how a website can be used as a search engine by
777   * consumers (e.g. web browsers).
778   *
779   * @since 28.2
780   */
781  public static final MediaType OPENSEARCH_DESCRIPTION_UTF_8 =
782      createConstantUtf8(APPLICATION_TYPE, "opensearchdescription+xml");
783
784  /** <a href="https://www.rfc-editor.org/rfc/rfc3778.html">PDF</a>. */
785  public static final MediaType PDF = createConstant(APPLICATION_TYPE, "pdf");
786
787  /** <a href="https://www.rfc-editor.org/rfc/rfc2046.html#section-4.5.2">PostScript</a>. */
788  public static final MediaType POSTSCRIPT = createConstant(APPLICATION_TYPE, "postscript");
789
790  /**
791   * <a href="http://tools.ietf.org/html/draft-rfernando-protocol-buffers-00">Protocol buffers</a>
792   *
793   * @since 15.0
794   */
795  public static final MediaType PROTOBUF = createConstant(APPLICATION_TYPE, "protobuf");
796
797  /**
798   * <a href="https://en.wikipedia.org/wiki/RDF/XML">RDF/XML</a> documents, which are XML
799   * serializations of <a
800   * href="https://en.wikipedia.org/wiki/Resource_Description_Framework">Resource Description
801   * Framework</a> graphs.
802   *
803   * @since 14.0
804   */
805  public static final MediaType RDF_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rdf+xml");
806
807  /** <a href="https://www.iana.org/assignments/media-types/application/rtf">RTF</a>. */
808  public static final MediaType RTF_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rtf");
809
810  /**
811   * <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares {@link #FONT_SFNT
812   * font/sfnt} to be the correct media type for SFNT, but this may be necessary in certain
813   * situations for compatibility.
814   *
815   * @since 17.0
816   */
817  public static final MediaType SFNT = createConstant(APPLICATION_TYPE, "font-sfnt");
818
819  /** Adobe Flash content. */
820  public static final MediaType SHOCKWAVE_FLASH =
821      createConstant(APPLICATION_TYPE, "x-shockwave-flash");
822
823  /**
824   * {@code skp} files produced by the 3D Modeling software <a
825   * href="https://www.sketchup.com/">SketchUp</a>
826   *
827   * @since 13.0
828   */
829  public static final MediaType SKETCHUP = createConstant(APPLICATION_TYPE, "vnd.sketchup.skp");
830
831  /**
832   * As described in <a href="http://www.ietf.org/rfc/rfc3902.txt">RFC 3902</a>, this constant
833   * ({@code application/soap+xml}) is used to identify SOAP 1.2 message envelopes that have been
834   * serialized with XML 1.0.
835   *
836   * <p>For SOAP 1.1 messages, see {@code XML_UTF_8} per <a
837   * href="https://www.w3.org/TR/2000/NOTE-SOAP-20000508/">W3C Note on Simple Object Access Protocol
838   * (SOAP) 1.1</a>
839   *
840   * @since 20.0
841   */
842  public static final MediaType SOAP_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "soap+xml");
843
844  /** tar archives. */
845  public static final MediaType TAR = createConstant(APPLICATION_TYPE, "x-tar");
846
847  /**
848   * <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares {@link #FONT_WOFF
849   * font/woff} to be the correct media type for WOFF, but this may be necessary in certain
850   * situations for compatibility.
851   *
852   * @since 17.0
853   */
854  public static final MediaType WOFF = createConstant(APPLICATION_TYPE, "font-woff");
855
856  /**
857   * <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares {@link #FONT_WOFF2
858   * font/woff2} to be the correct media type for WOFF2, but this may be necessary in certain
859   * situations for compatibility.
860   *
861   * @since 20.0
862   */
863  public static final MediaType WOFF2 = createConstant(APPLICATION_TYPE, "font-woff2");
864
865  /** <a href="https://www.rfc-editor.org/rfc/rfc3236.html">XHTML</a>. */
866  public static final MediaType XHTML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xhtml+xml");
867
868  /**
869   * Extensible Resource Descriptors. This is not yet registered with the IANA, but it is specified
870   * by OASIS in the <a href="http://docs.oasis-open.org/xri/xrd/v1.0/cd02/xrd-1.0-cd02.html">XRD
871   * definition</a> and implemented in projects such as <a
872   * href="http://code.google.com/p/webfinger/">WebFinger</a>.
873   *
874   * @since 14.0
875   */
876  public static final MediaType XRD_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xrd+xml");
877
878  /** <a href="https://www.iana.org/assignments/media-types/application/zip">ZIP archives</a>. */
879  public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip");
880
881  /* font types */
882
883  /**
884   * A collection of font outlines as defined by <a href="https://tools.ietf.org/html/rfc8081">RFC
885   * 8081</a>.
886   *
887   * @since 30.0
888   */
889  public static final MediaType FONT_COLLECTION = createConstant(FONT_TYPE, "collection");
890
891  /**
892   * <a href="https://en.wikipedia.org/wiki/OpenType">Open Type Font Format</a> (OTF) as defined by
893   * <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a>.
894   *
895   * @since 30.0
896   */
897  public static final MediaType FONT_OTF = createConstant(FONT_TYPE, "otf");
898
899  /**
900   * <a href="https://en.wikipedia.org/wiki/SFNT">Spline or Scalable Font Format</a> (SFNT). <a
901   * href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct media
902   * type for SFNT, but {@link #SFNT application/font-sfnt} may be necessary in certain situations
903   * for compatibility.
904   *
905   * @since 30.0
906   */
907  public static final MediaType FONT_SFNT = createConstant(FONT_TYPE, "sfnt");
908
909  /**
910   * <a href="https://en.wikipedia.org/wiki/TrueType">True Type Font Format</a> (TTF) as defined by
911   * <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a>.
912   *
913   * @since 30.0
914   */
915  public static final MediaType FONT_TTF = createConstant(FONT_TYPE, "ttf");
916
917  /**
918   * <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font Format</a> (WOFF). <a
919   * href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct media
920   * type for SFNT, but {@link #WOFF application/font-woff} may be necessary in certain situations
921   * for compatibility.
922   *
923   * @since 30.0
924   */
925  public static final MediaType FONT_WOFF = createConstant(FONT_TYPE, "woff");
926
927  /**
928   * <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font Format</a> (WOFF2).
929   * <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct
930   * media type for SFNT, but {@link #WOFF2 application/font-woff2} may be necessary in certain
931   * situations for compatibility.
932   *
933   * @since 30.0
934   */
935  public static final MediaType FONT_WOFF2 = createConstant(FONT_TYPE, "woff2");
936
937  /**
938   * <a href="https://yaml.org">YAML</a>, as standardized in <a
939   * href="https://www.rfc-editor.org/info/rfc9512">RFC 9512</a>.
940   *
941   * @since 33.7.0
942   */
943  public static final MediaType YAML = createConstant(APPLICATION_TYPE, "yaml");
944
945  private final String type;
946  private final String subtype;
947  private final ImmutableListMultimap<String, String> parameters;
948
949  @LazyInit private @Nullable String toString;
950
951  @LazyInit private int hashCode;
952
953  // We need to differentiate between "not computed" and "computed to be absent."
954  @SuppressWarnings("NullableOptional")
955  @LazyInit
956  private @Nullable Optional<Charset> parsedCharset;
957
958  private MediaType(String type, String subtype, ImmutableListMultimap<String, String> parameters) {
959    this.type = type;
960    this.subtype = subtype;
961    this.parameters = parameters;
962  }
963
964  /** Returns the top-level media type. For example, {@code "text"} in {@code "text/plain"}. */
965  public String type() {
966    return type;
967  }
968
969  /** Returns the media subtype. For example, {@code "plain"} in {@code "text/plain"}. */
970  public String subtype() {
971    return subtype;
972  }
973
974  /** Returns a multimap containing the parameters of this media type. */
975  public ImmutableListMultimap<String, String> parameters() {
976    return parameters;
977  }
978
979  private Map<String, ImmutableMultiset<String>> parametersAsMap() {
980    return transformValues(parameters.asMap(), ImmutableMultiset::copyOf);
981  }
982
983  /**
984   * Returns an optional charset for the value of the charset parameter if it is specified.
985   *
986   * @throws IllegalStateException if multiple charset values have been set for this media type
987   * @throws IllegalCharsetNameException if a charset value is present, but illegal
988   * @throws UnsupportedCharsetException if a charset value is present, but no support is available
989   *     in this instance of the Java virtual machine
990   */
991  public Optional<Charset> charset() {
992    // racy single-check idiom, this is safe because Optional is immutable.
993    Optional<Charset> local = parsedCharset;
994    if (local == null) {
995      String value = null;
996      local = Optional.absent();
997      for (String currentValue : parameters.get(CHARSET_ATTRIBUTE)) {
998        if (value == null) {
999          value = currentValue;
1000          local = Optional.of(Charset.forName(value));
1001        } else if (!value.equals(currentValue)) {
1002          throw new IllegalStateException(
1003              "Multiple charset values defined: " + value + ", " + currentValue);
1004        }
1005      }
1006      parsedCharset = local;
1007    }
1008    return local;
1009  }
1010
1011  /**
1012   * Returns a new instance with the same type and subtype as this instance, but without any
1013   * parameters.
1014   */
1015  public MediaType withoutParameters() {
1016    return parameters.isEmpty() ? this : create(type, subtype);
1017  }
1018
1019  /**
1020   * <em>Replaces</em> all parameters with the given parameters.
1021   *
1022   * @throws IllegalArgumentException if any parameter or value is invalid
1023   */
1024  public MediaType withParameters(Multimap<String, String> parameters) {
1025    return create(type, subtype, parameters);
1026  }
1027
1028  /**
1029   * <em>Replaces</em> all parameters with the given attribute with parameters using the given
1030   * values. If there are no values, any existing parameters with the given attribute are removed.
1031   *
1032   * @throws IllegalArgumentException if either {@code attribute} or {@code values} is invalid
1033   * @since 24.0
1034   */
1035  public MediaType withParameters(String attribute, Iterable<String> values) {
1036    checkNotNull(attribute);
1037    checkNotNull(values);
1038    String normalizedAttribute = normalizeToken(attribute);
1039    ImmutableListMultimap.Builder<String, String> updatedParameters =
1040        ImmutableListMultimap.builder();
1041    for (Entry<String, String> entry : parameters.entries()) {
1042      String key = entry.getKey();
1043      if (!normalizedAttribute.equals(key)) {
1044        updatedParameters.put(key, entry.getValue());
1045      }
1046    }
1047    for (String value : values) {
1048      updatedParameters.put(
1049          normalizedAttribute, normalizeParameterValue(normalizedAttribute, value));
1050    }
1051    MediaType mediaType = new MediaType(type, subtype, updatedParameters.build());
1052    // if the attribute isn't charset, we can just inherit the current parsedCharset
1053    if (!normalizedAttribute.equals(CHARSET_ATTRIBUTE)) {
1054      mediaType.parsedCharset = this.parsedCharset;
1055    }
1056    // Return one of the constants if the media type is a known type.
1057    @SuppressWarnings("GetOrDefaultNotNull") // getOrDefault requires API Level 24
1058    MediaType result = firstNonNull(knownTypes.get(mediaType), mediaType);
1059    return result;
1060  }
1061
1062  /**
1063   * <em>Replaces</em> all parameters with the given attribute with a single parameter with the
1064   * given value. If multiple parameters with the same attributes are necessary use {@link
1065   * #withParameters(String, Iterable)}. Prefer {@link #withCharset} for setting the {@code charset}
1066   * parameter when using a {@link Charset} object.
1067   *
1068   * @throws IllegalArgumentException if either {@code attribute} or {@code value} is invalid
1069   */
1070  public MediaType withParameter(String attribute, String value) {
1071    return withParameters(attribute, ImmutableSet.of(value));
1072  }
1073
1074  /**
1075   * Returns a new instance with the same type and subtype as this instance, with the {@code
1076   * charset} parameter set to the {@link Charset#name name} of the given charset. Only one {@code
1077   * charset} parameter will be present on the new instance regardless of the number set on this
1078   * one.
1079   *
1080   * <p>If a charset must be specified that is not supported on this JVM (and thus is not
1081   * representable as a {@link Charset} instance), use {@link #withParameter}.
1082   */
1083  public MediaType withCharset(Charset charset) {
1084    checkNotNull(charset);
1085    MediaType withCharset = withParameter(CHARSET_ATTRIBUTE, charset.name());
1086    // precache the charset so we don't need to parse it
1087    withCharset.parsedCharset = Optional.of(charset);
1088    return withCharset;
1089  }
1090
1091  /** Returns true if either the type or subtype is the wildcard. */
1092  public boolean hasWildcard() {
1093    return type.equals(WILDCARD) || subtype.equals(WILDCARD);
1094  }
1095
1096  /**
1097   * Returns {@code true} if this instance falls within the range (as defined by <a
1098   * href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">the HTTP Accept header</a>)
1099   * given by the argument according to three criteria:
1100   *
1101   * <ol>
1102   *   <li>The type of the argument is the wildcard or equal to the type of this instance.
1103   *   <li>The subtype of the argument is the wildcard or equal to the subtype of this instance.
1104   *   <li>All of the parameters present in the argument are present in this instance.
1105   * </ol>
1106   *
1107   * <p>For example:
1108   *
1109   * {@snippet :
1110   * PLAIN_TEXT_UTF_8.is(PLAIN_TEXT_UTF_8) // true
1111   * PLAIN_TEXT_UTF_8.is(HTML_UTF_8) // false
1112   * PLAIN_TEXT_UTF_8.is(ANY_TYPE) // true
1113   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE) // true
1114   * PLAIN_TEXT_UTF_8.is(ANY_IMAGE_TYPE) // false
1115   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_8)) // true
1116   * PLAIN_TEXT_UTF_8.withoutParameters().is(ANY_TEXT_TYPE.withCharset(UTF_8)) // false
1117   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_16)) // false
1118   * }
1119   *
1120   * <p>Note that while it is possible to have the same parameter declared multiple times within a
1121   * media type this method does not consider the number of occurrences of a parameter. For example,
1122   * {@code "text/plain; charset=UTF-8"} satisfies {@code "text/plain; charset=UTF-8;
1123   * charset=UTF-8"}.
1124   */
1125  public boolean is(MediaType mediaTypeRange) {
1126    return (mediaTypeRange.type.equals(WILDCARD) || mediaTypeRange.type.equals(this.type))
1127        && (mediaTypeRange.subtype.equals(WILDCARD) || mediaTypeRange.subtype.equals(this.subtype))
1128        && this.parameters.entries().containsAll(mediaTypeRange.parameters.entries());
1129  }
1130
1131  /**
1132   * Creates a new media type with the given type and subtype.
1133   *
1134   * @throws IllegalArgumentException if type or subtype is invalid or if a wildcard is used for the
1135   *     type, but not the subtype.
1136   */
1137  public static MediaType create(String type, String subtype) {
1138    MediaType mediaType = create(type, subtype, ImmutableListMultimap.of());
1139    mediaType.parsedCharset = Optional.absent();
1140    return mediaType;
1141  }
1142
1143  private static MediaType create(
1144      String type, String subtype, Multimap<String, String> parameters) {
1145    checkNotNull(type);
1146    checkNotNull(subtype);
1147    checkNotNull(parameters);
1148    String normalizedType = normalizeToken(type);
1149    String normalizedSubtype = normalizeToken(subtype);
1150    checkArgument(
1151        !normalizedType.equals(WILDCARD) || normalizedSubtype.equals(WILDCARD),
1152        "A wildcard type cannot be used with a non-wildcard subtype");
1153    ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder();
1154    for (Entry<String, String> entry : parameters.entries()) {
1155      String attribute = normalizeToken(entry.getKey());
1156      builder.put(attribute, normalizeParameterValue(attribute, entry.getValue()));
1157    }
1158    MediaType mediaType = new MediaType(normalizedType, normalizedSubtype, builder.build());
1159    // Return one of the constants if the media type is a known type.
1160    @SuppressWarnings("GetOrDefaultNotNull") // getOrDefault requires API Level 24
1161    MediaType result = firstNonNull(knownTypes.get(mediaType), mediaType);
1162    return result;
1163  }
1164
1165  /**
1166   * Creates a media type with the "application" type and the given subtype.
1167   *
1168   * @throws IllegalArgumentException if subtype is invalid
1169   */
1170  static MediaType createApplicationType(String subtype) {
1171    return create(APPLICATION_TYPE, subtype);
1172  }
1173
1174  /**
1175   * Creates a media type with the "audio" type and the given subtype.
1176   *
1177   * @throws IllegalArgumentException if subtype is invalid
1178   */
1179  static MediaType createAudioType(String subtype) {
1180    return create(AUDIO_TYPE, subtype);
1181  }
1182
1183  /**
1184   * Creates a media type with the "font" type and the given subtype.
1185   *
1186   * @throws IllegalArgumentException if subtype is invalid
1187   */
1188  static MediaType createFontType(String subtype) {
1189    return create(FONT_TYPE, subtype);
1190  }
1191
1192  /**
1193   * Creates a media type with the "image" type and the given subtype.
1194   *
1195   * @throws IllegalArgumentException if subtype is invalid
1196   */
1197  static MediaType createImageType(String subtype) {
1198    return create(IMAGE_TYPE, subtype);
1199  }
1200
1201  /**
1202   * Creates a media type with the "text" type and the given subtype.
1203   *
1204   * @throws IllegalArgumentException if subtype is invalid
1205   */
1206  static MediaType createTextType(String subtype) {
1207    return create(TEXT_TYPE, subtype);
1208  }
1209
1210  /**
1211   * Creates a media type with the "video" type and the given subtype.
1212   *
1213   * @throws IllegalArgumentException if subtype is invalid
1214   */
1215  static MediaType createVideoType(String subtype) {
1216    return create(VIDEO_TYPE, subtype);
1217  }
1218
1219  private static String normalizeToken(String token) {
1220    checkArgument(TOKEN_MATCHER.matchesAllOf(token));
1221    checkArgument(!token.isEmpty());
1222    return Ascii.toLowerCase(token);
1223  }
1224
1225  private static String normalizeParameterValue(String attribute, String value) {
1226    checkNotNull(value); // for GWT
1227    checkArgument(ascii().matchesAllOf(value), "parameter values must be ASCII: %s", value);
1228    return attribute.equals(CHARSET_ATTRIBUTE) ? Ascii.toLowerCase(value) : value;
1229  }
1230
1231  /**
1232   * Parses a media type from its string representation.
1233   *
1234   * @throws IllegalArgumentException if the input is not parsable
1235   */
1236  @CanIgnoreReturnValue // TODO(b/219820829): consider removing
1237  public static MediaType parse(String input) {
1238    checkNotNull(input);
1239    Tokenizer tokenizer = new Tokenizer(input);
1240    try {
1241      String type = tokenizer.consumeToken(TOKEN_MATCHER);
1242      consumeSeparator(tokenizer, '/');
1243      String subtype = tokenizer.consumeToken(TOKEN_MATCHER);
1244      ImmutableListMultimap.Builder<String, String> parameters = ImmutableListMultimap.builder();
1245      while (tokenizer.hasMore()) {
1246        consumeSeparator(tokenizer, ';');
1247        String attribute = tokenizer.consumeToken(TOKEN_MATCHER);
1248        consumeSeparator(tokenizer, '=');
1249        String value;
1250        if (tokenizer.previewChar() == '"') {
1251          tokenizer.consumeCharacter('"');
1252          StringBuilder valueBuilder = new StringBuilder();
1253          while (tokenizer.previewChar() != '"') {
1254            if (tokenizer.previewChar() == '\\') {
1255              tokenizer.consumeCharacter('\\');
1256              valueBuilder.append(tokenizer.consumeCharacter(ascii()));
1257            } else {
1258              valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER));
1259            }
1260          }
1261          value = valueBuilder.toString();
1262          tokenizer.consumeCharacter('"');
1263        } else {
1264          value = tokenizer.consumeToken(TOKEN_MATCHER);
1265        }
1266        parameters.put(attribute, value);
1267      }
1268      return create(type, subtype, parameters.build());
1269    } catch (IllegalStateException e) {
1270      throw new IllegalArgumentException("Could not parse '" + input + "'", e);
1271    }
1272  }
1273
1274  private static void consumeSeparator(Tokenizer tokenizer, char c) {
1275    tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE);
1276    tokenizer.consumeCharacter(c);
1277    tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE);
1278  }
1279
1280  private static final class Tokenizer {
1281    final String input;
1282    int position = 0;
1283
1284    Tokenizer(String input) {
1285      this.input = input;
1286    }
1287
1288    @CanIgnoreReturnValue
1289    String consumeTokenIfPresent(CharMatcher matcher) {
1290      checkState(hasMore());
1291      int startPosition = position;
1292      position = matcher.negate().indexIn(input, startPosition);
1293      return hasMore() ? input.substring(startPosition, position) : input.substring(startPosition);
1294    }
1295
1296    String consumeToken(CharMatcher matcher) {
1297      int startPosition = position;
1298      String token = consumeTokenIfPresent(matcher);
1299      checkState(position != startPosition);
1300      return token;
1301    }
1302
1303    char consumeCharacter(CharMatcher matcher) {
1304      checkState(hasMore());
1305      char c = previewChar();
1306      checkState(matcher.matches(c));
1307      position++;
1308      return c;
1309    }
1310
1311    @CanIgnoreReturnValue
1312    char consumeCharacter(char c) {
1313      checkState(hasMore());
1314      checkState(previewChar() == c);
1315      position++;
1316      return c;
1317    }
1318
1319    char previewChar() {
1320      checkState(hasMore());
1321      return input.charAt(position);
1322    }
1323
1324    boolean hasMore() {
1325      return (position >= 0) && (position < input.length());
1326    }
1327  }
1328
1329  @Override
1330  public boolean equals(@Nullable Object obj) {
1331    if (obj == this) {
1332      return true;
1333    } else if (obj instanceof MediaType) {
1334      MediaType that = (MediaType) obj;
1335      return this.type.equals(that.type)
1336          && this.subtype.equals(that.subtype)
1337          // compare parameters regardless of order
1338          && this.parametersAsMap().equals(that.parametersAsMap());
1339    } else {
1340      return false;
1341    }
1342  }
1343
1344  @Override
1345  public int hashCode() {
1346    // racy single-check idiom
1347    int h = hashCode;
1348    if (h == 0) {
1349      h = hash(type, subtype, parametersAsMap());
1350      hashCode = h;
1351    }
1352    return h;
1353  }
1354
1355  private static final MapJoiner PARAMETER_JOINER = Joiner.on("; ").withKeyValueSeparator("=");
1356
1357  /**
1358   * Returns the string representation of this media type in the format described in <a
1359   * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.
1360   */
1361  @Override
1362  public String toString() {
1363    // racy single-check idiom, safe because String is immutable
1364    String result = toString;
1365    if (result == null) {
1366      result = computeToString();
1367      toString = result;
1368    }
1369    return result;
1370  }
1371
1372  private String computeToString() {
1373    StringBuilder builder = new StringBuilder().append(type).append('/').append(subtype);
1374    if (!parameters.isEmpty()) {
1375      builder.append("; ");
1376      Multimap<String, String> quotedParameters =
1377          Multimaps.transformValues(
1378              parameters,
1379              (String value) ->
1380                  (TOKEN_MATCHER.matchesAllOf(value) && !value.isEmpty())
1381                      ? value
1382                      : escapeAndQuote(value));
1383      PARAMETER_JOINER.appendTo(builder, quotedParameters.entries());
1384    }
1385    return builder.toString();
1386  }
1387
1388  private static String escapeAndQuote(String value) {
1389    StringBuilder escaped = new StringBuilder(value.length() + 16).append('"');
1390    for (int i = 0; i < value.length(); i++) {
1391      char ch = value.charAt(i);
1392      if (ch == '\r' || ch == '\\' || ch == '"') {
1393        escaped.append('\\');
1394      }
1395      escaped.append(ch);
1396    }
1397    return escaped.append('"').toString();
1398  }
1399}