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