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