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