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