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.Charsets.UTF_8; 020import static com.google.common.base.Preconditions.checkArgument; 021import static com.google.common.base.Preconditions.checkNotNull; 022import static com.google.common.base.Preconditions.checkState; 023 024import com.google.common.annotations.Beta; 025import com.google.common.annotations.GwtCompatible; 026import com.google.common.base.Ascii; 027import com.google.common.base.CharMatcher; 028import com.google.common.base.Function; 029import com.google.common.base.Joiner; 030import com.google.common.base.Joiner.MapJoiner; 031import com.google.common.base.MoreObjects; 032import com.google.common.base.Objects; 033import com.google.common.base.Optional; 034import com.google.common.collect.ImmutableListMultimap; 035import com.google.common.collect.ImmutableMultiset; 036import com.google.common.collect.ImmutableSet; 037import com.google.common.collect.Maps; 038import com.google.common.collect.Multimap; 039import com.google.common.collect.Multimaps; 040import com.google.errorprone.annotations.Immutable; 041import com.google.errorprone.annotations.concurrent.LazyInit; 042import java.nio.charset.Charset; 043import java.nio.charset.IllegalCharsetNameException; 044import java.nio.charset.UnsupportedCharsetException; 045import java.util.Collection; 046import java.util.Map; 047import java.util.Map.Entry; 048import org.checkerframework.checker.nullness.compatqual.NullableDecl; 049 050/** 051 * Represents an <a href="http://en.wikipedia.org/wiki/Internet_media_type">Internet Media Type</a> 052 * (also known as a MIME Type or Content Type). This class also supports the concept of media ranges 053 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">defined by HTTP/1.1</a>. 054 * As such, the {@code *} character is treated as a wildcard and is used to represent any acceptable 055 * type or subtype value. A media type may not have wildcard type with a declared subtype. The 056 * {@code *} character has no special meaning as part of a parameter. All values for type, subtype, 057 * parameter attributes or parameter values must be valid according to RFCs <a 058 * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a 059 * href="http://www.ietf.org/rfc/rfc2046.txt">2046</a>. 060 * 061 * <p>All portions of the media type that are case-insensitive (type, subtype, parameter attributes) 062 * are normalized to lowercase. The value of the {@code charset} parameter is normalized to 063 * lowercase, but all others are left as-is. 064 * 065 * <p>Note that this specifically does <strong>not</strong> represent the value of the MIME {@code 066 * Content-Type} header and as such has no support for header-specific considerations such as line 067 * folding and comments. 068 * 069 * <p>For media types that take a charset the predefined constants default to UTF-8 and have a 070 * "_UTF_8" suffix. To get a version without a character set, use {@link #withoutParameters}. 071 * 072 * @since 12.0 073 * @author Gregory Kick 074 */ 075@Beta 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 QUOTED_TEXT_MATCHER = ascii().and(CharMatcher.noneOf("\"\\\r")); 091 /* 092 * This matches the same characters as linear-white-space from RFC 822, but we make no effort to 093 * enforce any particular rules with regards to line folding as stated in the class docs. 094 */ 095 private static final CharMatcher LINEAR_WHITE_SPACE = CharMatcher.anyOf(" \t\r\n"); 096 097 // TODO(gak): make these public? 098 private static final String APPLICATION_TYPE = "application"; 099 private static final String AUDIO_TYPE = "audio"; 100 private static final String IMAGE_TYPE = "image"; 101 private static final String TEXT_TYPE = "text"; 102 private static final String VIDEO_TYPE = "video"; 103 104 private static final String WILDCARD = "*"; 105 106 private static final Map<MediaType, MediaType> KNOWN_TYPES = Maps.newHashMap(); 107 108 private static MediaType createConstant(String type, String subtype) { 109 MediaType mediaType = 110 addKnownType(new MediaType(type, subtype, ImmutableListMultimap.<String, String>of())); 111 mediaType.parsedCharset = Optional.absent(); 112 return mediaType; 113 } 114 115 private static MediaType createConstantUtf8(String type, String subtype) { 116 MediaType mediaType = addKnownType(new MediaType(type, subtype, UTF_8_CONSTANT_PARAMETERS)); 117 mediaType.parsedCharset = Optional.of(UTF_8); 118 return mediaType; 119 } 120 121 private static MediaType addKnownType(MediaType mediaType) { 122 KNOWN_TYPES.put(mediaType, mediaType); 123 return mediaType; 124 } 125 126 /* 127 * The following constants are grouped by their type and ordered alphabetically by the constant 128 * name within that type. The constant name should be a sensible identifier that is closest to the 129 * "common name" of the media. This is often, but not necessarily the same as the subtype. 130 * 131 * Be sure to declare all constants with the type and subtype in all lowercase. For types that 132 * take a charset (e.g. all text/* types), default to UTF-8 and suffix the constant name with 133 * "_UTF_8". 134 */ 135 136 public static final MediaType ANY_TYPE = createConstant(WILDCARD, WILDCARD); 137 public static final MediaType ANY_TEXT_TYPE = createConstant(TEXT_TYPE, WILDCARD); 138 public static final MediaType ANY_IMAGE_TYPE = createConstant(IMAGE_TYPE, WILDCARD); 139 public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD); 140 public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD); 141 public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD); 142 143 /* text types */ 144 public static final MediaType CACHE_MANIFEST_UTF_8 = 145 createConstantUtf8(TEXT_TYPE, "cache-manifest"); 146 public static final MediaType CSS_UTF_8 = createConstantUtf8(TEXT_TYPE, "css"); 147 public static final MediaType CSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "csv"); 148 public static final MediaType HTML_UTF_8 = createConstantUtf8(TEXT_TYPE, "html"); 149 public static final MediaType I_CALENDAR_UTF_8 = createConstantUtf8(TEXT_TYPE, "calendar"); 150 public static final MediaType PLAIN_TEXT_UTF_8 = createConstantUtf8(TEXT_TYPE, "plain"); 151 /** 152 * <a href="http://www.rfc-editor.org/rfc/rfc4329.txt">RFC 4329</a> declares {@link 153 * #JAVASCRIPT_UTF_8 application/javascript} to be the correct media type for JavaScript, but this 154 * may be necessary in certain situations for compatibility. 155 */ 156 public static final MediaType TEXT_JAVASCRIPT_UTF_8 = createConstantUtf8(TEXT_TYPE, "javascript"); 157 /** 158 * <a href="http://www.iana.org/assignments/media-types/text/tab-separated-values">Tab separated 159 * values</a>. 160 * 161 * @since 15.0 162 */ 163 public static final MediaType TSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "tab-separated-values"); 164 165 public static final MediaType VCARD_UTF_8 = createConstantUtf8(TEXT_TYPE, "vcard"); 166 public static final MediaType WML_UTF_8 = createConstantUtf8(TEXT_TYPE, "vnd.wap.wml"); 167 /** 168 * As described in <a href="http://www.ietf.org/rfc/rfc3023.txt">RFC 3023</a>, this constant 169 * ({@code text/xml}) is used for XML documents that are "readable by casual users." {@link 170 * #APPLICATION_XML_UTF_8} is provided for documents that are intended for applications. 171 */ 172 public static final MediaType XML_UTF_8 = createConstantUtf8(TEXT_TYPE, "xml"); 173 /** 174 * As described in <a href="https://w3c.github.io/webvtt/#iana-text-vtt">the VTT spec</a>, this is 175 * used for Web Video Text Tracks (WebVTT) files, used with the HTML5 track element. 176 * 177 * @since 20.0 178 */ 179 public static final MediaType VTT_UTF_8 = createConstantUtf8(TEXT_TYPE, "vtt"); 180 181 /* image types */ 182 public static final MediaType BMP = createConstant(IMAGE_TYPE, "bmp"); 183 /** 184 * The media type for the <a href="http://en.wikipedia.org/wiki/Camera_Image_File_Format">Canon 185 * Image File Format</a> ({@code crw} files), a widely-used "raw image" format for cameras. It is 186 * found in {@code /etc/mime.types}, e.g. in <a href= 187 * "http://anonscm.debian.org/gitweb/?p=collab-maint/mime-support.git;a=blob;f=mime.types;hb=HEAD" 188 * >Debian 3.48-1</a>. 189 * 190 * @since 15.0 191 */ 192 public static final MediaType CRW = createConstant(IMAGE_TYPE, "x-canon-crw"); 193 194 public static final MediaType GIF = createConstant(IMAGE_TYPE, "gif"); 195 public static final MediaType ICO = createConstant(IMAGE_TYPE, "vnd.microsoft.icon"); 196 public static final MediaType JPEG = createConstant(IMAGE_TYPE, "jpeg"); 197 public static final MediaType PNG = createConstant(IMAGE_TYPE, "png"); 198 /** 199 * The media type for the Photoshop File Format ({@code psd} files) as defined by <a 200 * href="http://www.iana.org/assignments/media-types/image/vnd.adobe.photoshop">IANA</a>, and 201 * found in {@code /etc/mime.types}, e.g. <a 202 * href="http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types"></a> of the 203 * Apache <a href="http://httpd.apache.org/">HTTPD project</a>; for the specification, see <a 204 * href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm"> 205 * Adobe Photoshop Document Format</a> and <a 206 * href="http://en.wikipedia.org/wiki/Adobe_Photoshop#File_format">Wikipedia</a>; this is the 207 * regular output/input of Photoshop (which can also export to various image formats; note that 208 * files with extension "PSB" are in a distinct but related format). 209 * 210 * <p>This is a more recent replacement for the older, experimental type {@code x-photoshop}: <a 211 * href="http://tools.ietf.org/html/rfc2046#section-6">RFC-2046.6</a>. 212 * 213 * @since 15.0 214 */ 215 public static final MediaType PSD = createConstant(IMAGE_TYPE, "vnd.adobe.photoshop"); 216 217 public static final MediaType SVG_UTF_8 = createConstantUtf8(IMAGE_TYPE, "svg+xml"); 218 public static final MediaType TIFF = createConstant(IMAGE_TYPE, "tiff"); 219 public static final MediaType WEBP = createConstant(IMAGE_TYPE, "webp"); 220 221 /* audio types */ 222 public static final MediaType MP4_AUDIO = createConstant(AUDIO_TYPE, "mp4"); 223 public static final MediaType MPEG_AUDIO = createConstant(AUDIO_TYPE, "mpeg"); 224 public static final MediaType OGG_AUDIO = createConstant(AUDIO_TYPE, "ogg"); 225 public static final MediaType WEBM_AUDIO = createConstant(AUDIO_TYPE, "webm"); 226 227 /** 228 * Media type for L24 audio, as defined by <a href="https://tools.ietf.org/html/rfc3190">RFC 229 * 3190</a>. 230 * 231 * @since 20.0 232 */ 233 public static final MediaType L24_AUDIO = createConstant(AUDIO_TYPE, "l24"); 234 235 /** 236 * Media type for Basic Audio, as defined by <a 237 * href="http://tools.ietf.org/html/rfc2046#section-4.3">RFC 2046</a>. 238 * 239 * @since 20.0 240 */ 241 public static final MediaType BASIC_AUDIO = createConstant(AUDIO_TYPE, "basic"); 242 243 /** 244 * Media type for Advanced Audio Coding. For more information, see <a 245 * href="https://en.wikipedia.org/wiki/Advanced_Audio_Coding">Advanced Audio Coding</a>. 246 * 247 * @since 20.0 248 */ 249 public static final MediaType AAC_AUDIO = createConstant(AUDIO_TYPE, "aac"); 250 251 /** 252 * Media type for Vorbis Audio, as defined by <a href="http://tools.ietf.org/html/rfc5215">RFC 253 * 5215</a>. 254 * 255 * @since 20.0 256 */ 257 public static final MediaType VORBIS_AUDIO = createConstant(AUDIO_TYPE, "vorbis"); 258 259 /** 260 * Media type for Windows Media Audio. For more information, see <a 261 * href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd562994(v=vs.85).aspx">file 262 * name extensions for Windows Media metafiles</a>. 263 * 264 * @since 20.0 265 */ 266 public static final MediaType WMA_AUDIO = createConstant(AUDIO_TYPE, "x-ms-wma"); 267 268 /** 269 * Media type for Windows Media metafiles. For more information, see <a 270 * href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd562994(v=vs.85).aspx">file 271 * name extensions for Windows Media metafiles</a>. 272 * 273 * @since 20.0 274 */ 275 public static final MediaType WAX_AUDIO = createConstant(AUDIO_TYPE, "x-ms-wax"); 276 277 /** 278 * Media type for Real Audio. For more information, see <a 279 * href="http://service.real.com/help/faq/rp8/configrp8win.html">this link</a>. 280 * 281 * @since 20.0 282 */ 283 public static final MediaType VND_REAL_AUDIO = createConstant(AUDIO_TYPE, "vnd.rn-realaudio"); 284 285 /** 286 * Media type for WAVE format, as defined by <a href="https://tools.ietf.org/html/rfc2361">RFC 287 * 2361</a>. 288 * 289 * @since 20.0 290 */ 291 public static final MediaType VND_WAVE_AUDIO = createConstant(AUDIO_TYPE, "vnd.wave"); 292 293 /* video types */ 294 public static final MediaType MP4_VIDEO = createConstant(VIDEO_TYPE, "mp4"); 295 public static final MediaType MPEG_VIDEO = createConstant(VIDEO_TYPE, "mpeg"); 296 public static final MediaType OGG_VIDEO = createConstant(VIDEO_TYPE, "ogg"); 297 public static final MediaType QUICKTIME = createConstant(VIDEO_TYPE, "quicktime"); 298 public static final MediaType WEBM_VIDEO = createConstant(VIDEO_TYPE, "webm"); 299 public static final MediaType WMV = createConstant(VIDEO_TYPE, "x-ms-wmv"); 300 301 /** 302 * Media type for Flash video. For more information, see <a href= 303 * "http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7d48.html" 304 * >this link</a>. 305 * 306 * @since 20.0 307 */ 308 public static final MediaType FLV_VIDEO = createConstant(VIDEO_TYPE, "x-flv"); 309 310 /** 311 * Media type for the 3GP multimedia container format. For more information, see <a 312 * href="ftp://www.3gpp.org/tsg_sa/TSG_SA/TSGS_23/Docs/PDF/SP-040065.pdf#page=10">3GPP TS 313 * 26.244</a>. 314 * 315 * @since 20.0 316 */ 317 public static final MediaType THREE_GPP_VIDEO = createConstant(VIDEO_TYPE, "3gpp"); 318 319 /** 320 * Media type for the 3G2 multimedia container format. For more information, see <a 321 * href="http://www.3gpp2.org/Public_html/specs/C.S0050-B_v1.0_070521.pdf#page=16">3GPP2 322 * C.S0050-B</a>. 323 * 324 * @since 20.0 325 */ 326 public static final MediaType THREE_GPP2_VIDEO = createConstant(VIDEO_TYPE, "3gpp2"); 327 328 /* application types */ 329 /** 330 * As described in <a href="http://www.ietf.org/rfc/rfc3023.txt">RFC 3023</a>, this constant 331 * ({@code application/xml}) is used for XML documents that are "unreadable by casual users." 332 * {@link #XML_UTF_8} is provided for documents that may be read by users. 333 */ 334 public static final MediaType APPLICATION_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xml"); 335 336 public static final MediaType ATOM_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "atom+xml"); 337 public static final MediaType BZIP2 = createConstant(APPLICATION_TYPE, "x-bzip2"); 338 339 /** 340 * Media type for <a href="https://www.dartlang.org/articles/embedding-in-html/">dart files</a>. 341 * 342 * @since 19.0 343 */ 344 public static final MediaType DART_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "dart"); 345 346 /** 347 * Media type for <a href="https://goo.gl/2QoMvg">Apple Passbook</a>. 348 * 349 * @since 19.0 350 */ 351 public static final MediaType APPLE_PASSBOOK = 352 createConstant(APPLICATION_TYPE, "vnd.apple.pkpass"); 353 354 /** 355 * Media type for <a href="http://en.wikipedia.org/wiki/Embedded_OpenType">Embedded OpenType</a> 356 * fonts. This is <a 357 * href="http://www.iana.org/assignments/media-types/application/vnd.ms-fontobject">registered 358 * </a> with the IANA. 359 * 360 * @since 17.0 361 */ 362 public static final MediaType EOT = createConstant(APPLICATION_TYPE, "vnd.ms-fontobject"); 363 /** 364 * As described in the <a href="http://idpf.org/epub">International Digital Publishing Forum</a> 365 * EPUB is the distribution and interchange format standard for digital publications and 366 * documents. This media type is defined in the <a 367 * href="http://www.idpf.org/epub/30/spec/epub30-ocf.html">EPUB Open Container Format</a> 368 * specification. 369 * 370 * @since 15.0 371 */ 372 public static final MediaType EPUB = createConstant(APPLICATION_TYPE, "epub+zip"); 373 374 public static final MediaType FORM_DATA = 375 createConstant(APPLICATION_TYPE, "x-www-form-urlencoded"); 376 /** 377 * As described in <a href="https://www.rsa.com/rsalabs/node.asp?id=2138">PKCS #12: Personal 378 * Information Exchange Syntax Standard</a>, PKCS #12 defines an archive file format for storing 379 * many cryptography objects as a single file. 380 * 381 * @since 15.0 382 */ 383 public static final MediaType KEY_ARCHIVE = createConstant(APPLICATION_TYPE, "pkcs12"); 384 /** 385 * This is a non-standard media type, but is commonly used in serving hosted binary files as it is 386 * <a href="http://code.google.com/p/browsersec/wiki/Part2#Survey_of_content_sniffing_behaviors"> 387 * known not to trigger content sniffing in current browsers</a>. It <i>should not</i> be used in 388 * other situations as it is not specified by any RFC and does not appear in the <a 389 * href="http://www.iana.org/assignments/media-types">/IANA MIME Media Types</a> list. Consider 390 * {@link #OCTET_STREAM} for binary data that is not being served to a browser. 391 * 392 * @since 14.0 393 */ 394 public static final MediaType APPLICATION_BINARY = createConstant(APPLICATION_TYPE, "binary"); 395 396 public static final MediaType GZIP = createConstant(APPLICATION_TYPE, "x-gzip"); 397 /** 398 * <a href="http://www.rfc-editor.org/rfc/rfc4329.txt">RFC 4329</a> declares this to be the 399 * correct media type for JavaScript, but {@link #TEXT_JAVASCRIPT_UTF_8 text/javascript} may be 400 * necessary in certain situations for compatibility. 401 */ 402 public static final MediaType JAVASCRIPT_UTF_8 = 403 createConstantUtf8(APPLICATION_TYPE, "javascript"); 404 405 public static final MediaType JSON_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "json"); 406 /** 407 * Media type for the <a href="http://www.w3.org/TR/appmanifest/">Manifest for a web 408 * application</a>. 409 * 410 * @since 19.0 411 */ 412 public static final MediaType MANIFEST_JSON_UTF_8 = 413 createConstantUtf8(APPLICATION_TYPE, "manifest+json"); 414 415 public static final MediaType KML = createConstant(APPLICATION_TYPE, "vnd.google-earth.kml+xml"); 416 public static final MediaType KMZ = createConstant(APPLICATION_TYPE, "vnd.google-earth.kmz"); 417 public static final MediaType MBOX = createConstant(APPLICATION_TYPE, "mbox"); 418 419 /** 420 * Media type for <a href="http://goo.gl/1pGBFm">Apple over-the-air mobile configuration 421 * profiles</a>. 422 * 423 * @since 18.0 424 */ 425 public static final MediaType APPLE_MOBILE_CONFIG = 426 createConstant(APPLICATION_TYPE, "x-apple-aspen-config"); 427 428 public static final MediaType MICROSOFT_EXCEL = createConstant(APPLICATION_TYPE, "vnd.ms-excel"); 429 public static final MediaType MICROSOFT_POWERPOINT = 430 createConstant(APPLICATION_TYPE, "vnd.ms-powerpoint"); 431 public static final MediaType MICROSOFT_WORD = createConstant(APPLICATION_TYPE, "msword"); 432 433 /** 434 * Media type for NaCl applications. For more information see <a 435 * href="https://developer.chrome.com/native-client/devguide/coding/application-structure">the 436 * Developer Guide for Native Client Application Structure</a>. 437 * 438 * @since 20.0 439 */ 440 public static final MediaType NACL_APPLICATION = createConstant(APPLICATION_TYPE, "x-nacl"); 441 442 /** 443 * Media type for NaCl portable applications. For more information see <a 444 * href="https://developer.chrome.com/native-client/devguide/coding/application-structure">the 445 * Developer Guide for Native Client Application Structure</a>. 446 * 447 * @since 20.0 448 */ 449 public static final MediaType NACL_PORTABLE_APPLICATION = 450 createConstant(APPLICATION_TYPE, "x-pnacl"); 451 452 public static final MediaType OCTET_STREAM = createConstant(APPLICATION_TYPE, "octet-stream"); 453 454 public static final MediaType OGG_CONTAINER = createConstant(APPLICATION_TYPE, "ogg"); 455 public static final MediaType OOXML_DOCUMENT = 456 createConstant( 457 APPLICATION_TYPE, "vnd.openxmlformats-officedocument.wordprocessingml.document"); 458 public static final MediaType OOXML_PRESENTATION = 459 createConstant( 460 APPLICATION_TYPE, "vnd.openxmlformats-officedocument.presentationml.presentation"); 461 public static final MediaType OOXML_SHEET = 462 createConstant(APPLICATION_TYPE, "vnd.openxmlformats-officedocument.spreadsheetml.sheet"); 463 public static final MediaType OPENDOCUMENT_GRAPHICS = 464 createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.graphics"); 465 public static final MediaType OPENDOCUMENT_PRESENTATION = 466 createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.presentation"); 467 public static final MediaType OPENDOCUMENT_SPREADSHEET = 468 createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.spreadsheet"); 469 public static final MediaType OPENDOCUMENT_TEXT = 470 createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.text"); 471 public static final MediaType PDF = createConstant(APPLICATION_TYPE, "pdf"); 472 public static final MediaType POSTSCRIPT = createConstant(APPLICATION_TYPE, "postscript"); 473 /** 474 * <a href="http://tools.ietf.org/html/draft-rfernando-protocol-buffers-00">Protocol buffers</a> 475 * 476 * @since 15.0 477 */ 478 public static final MediaType PROTOBUF = createConstant(APPLICATION_TYPE, "protobuf"); 479 480 public static final MediaType RDF_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rdf+xml"); 481 public static final MediaType RTF_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rtf"); 482 /** 483 * Media type for SFNT fonts (which includes <a 484 * href="http://en.wikipedia.org/wiki/TrueType/">TrueType</a> and <a 485 * href="http://en.wikipedia.org/wiki/OpenType/">OpenType</a> fonts). This is <a 486 * href="http://www.iana.org/assignments/media-types/application/font-sfnt">registered</a> with 487 * the IANA. 488 * 489 * @since 17.0 490 */ 491 public static final MediaType SFNT = createConstant(APPLICATION_TYPE, "font-sfnt"); 492 493 public static final MediaType SHOCKWAVE_FLASH = 494 createConstant(APPLICATION_TYPE, "x-shockwave-flash"); 495 public static final MediaType SKETCHUP = createConstant(APPLICATION_TYPE, "vnd.sketchup.skp"); 496 /** 497 * As described in <a href="http://www.ietf.org/rfc/rfc3902.txt">RFC 3902</a>, this constant 498 * ({@code application/soap+xml}) is used to identify SOAP 1.2 message envelopes that have been 499 * serialized with XML 1.0. 500 * 501 * <p>For SOAP 1.1 messages, see {@code XML_UTF_8} per <a 502 * href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/">W3C Note on Simple Object Access Protocol 503 * (SOAP) 1.1</a> 504 * 505 * @since 20.0 506 */ 507 public static final MediaType SOAP_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "soap+xml"); 508 509 public static final MediaType TAR = createConstant(APPLICATION_TYPE, "x-tar"); 510 /** 511 * Media type for the <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font 512 * Format</a> (WOFF) <a href="http://www.w3.org/TR/WOFF/">defined</a> by the W3C. This is <a 513 * href="http://www.iana.org/assignments/media-types/application/font-woff">registered</a> with 514 * the IANA. 515 * 516 * @since 17.0 517 */ 518 public static final MediaType WOFF = createConstant(APPLICATION_TYPE, "font-woff"); 519 /** 520 * Media type for the <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font 521 * Format</a> (WOFF) version 2 <a href="https://www.w3.org/TR/WOFF2/">defined</a> by the W3C. 522 * 523 * @since 20.0 524 */ 525 public static final MediaType WOFF2 = createConstant(APPLICATION_TYPE, "font-woff2"); 526 527 public static final MediaType XHTML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xhtml+xml"); 528 /** 529 * Media type for Extensible Resource Descriptors. This is not yet registered with the IANA, but 530 * it is specified by OASIS in the <a 531 * href="http://docs.oasis-open.org/xri/xrd/v1.0/cd02/xrd-1.0-cd02.html">XRD definition</a> and 532 * implemented in projects such as <a href="http://code.google.com/p/webfinger/">WebFinger</a>. 533 */ 534 public static final MediaType XRD_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xrd+xml"); 535 536 public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip"); 537 538 private final String type; 539 private final String subtype; 540 private final ImmutableListMultimap<String, String> parameters; 541 542 @LazyInit private String toString; 543 544 @LazyInit private int hashCode; 545 546 @LazyInit private Optional<Charset> parsedCharset; 547 548 private MediaType(String type, String subtype, ImmutableListMultimap<String, String> parameters) { 549 this.type = type; 550 this.subtype = subtype; 551 this.parameters = parameters; 552 } 553 554 /** Returns the top-level media type. For example, {@code "text"} in {@code "text/plain"}. */ 555 public String type() { 556 return type; 557 } 558 559 /** Returns the media subtype. For example, {@code "plain"} in {@code "text/plain"}. */ 560 public String subtype() { 561 return subtype; 562 } 563 564 /** Returns a multimap containing the parameters of this media type. */ 565 public ImmutableListMultimap<String, String> parameters() { 566 return parameters; 567 } 568 569 private Map<String, ImmutableMultiset<String>> parametersAsMap() { 570 return Maps.transformValues( 571 parameters.asMap(), 572 new Function<Collection<String>, ImmutableMultiset<String>>() { 573 @Override 574 public ImmutableMultiset<String> apply(Collection<String> input) { 575 return ImmutableMultiset.copyOf(input); 576 } 577 }); 578 } 579 580 /** 581 * Returns an optional charset for the value of the charset parameter if it is specified. 582 * 583 * @throws IllegalStateException if multiple charset values have been set for this media type 584 * @throws IllegalCharsetNameException if a charset value is present, but illegal 585 * @throws UnsupportedCharsetException if a charset value is present, but no support is available 586 * in this instance of the Java virtual machine 587 */ 588 public Optional<Charset> charset() { 589 // racy single-check idiom, this is safe because Optional is immutable. 590 Optional<Charset> local = parsedCharset; 591 if (local == null) { 592 String value = null; 593 local = Optional.absent(); 594 for (String currentValue : parameters.get(CHARSET_ATTRIBUTE)) { 595 if (value == null) { 596 value = currentValue; 597 local = Optional.of(Charset.forName(value)); 598 } else if (!value.equals(currentValue)) { 599 throw new IllegalStateException( 600 "Multiple charset values defined: " + value + ", " + currentValue); 601 } 602 } 603 parsedCharset = local; 604 } 605 return local; 606 } 607 608 /** 609 * Returns a new instance with the same type and subtype as this instance, but without any 610 * parameters. 611 */ 612 public MediaType withoutParameters() { 613 return parameters.isEmpty() ? this : create(type, subtype); 614 } 615 616 /** 617 * <em>Replaces</em> all parameters with the given parameters. 618 * 619 * @throws IllegalArgumentException if any parameter or value is invalid 620 */ 621 public MediaType withParameters(Multimap<String, String> parameters) { 622 return create(type, subtype, parameters); 623 } 624 625 /** 626 * <em>Replaces</em> all parameters with the given attribute with parameters using the given 627 * values. If there are no values, any existing parameters with the given attribute are 628 * removed. 629 * 630 * @throws IllegalArgumentException if either {@code attribute} or {@code values} is invalid 631 * @since 24.0 632 */ 633 public MediaType withParameters(String attribute, Iterable<String> values) { 634 checkNotNull(attribute); 635 checkNotNull(values); 636 String normalizedAttribute = normalizeToken(attribute); 637 ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder(); 638 for (Entry<String, String> entry : parameters.entries()) { 639 String key = entry.getKey(); 640 if (!normalizedAttribute.equals(key)) { 641 builder.put(key, entry.getValue()); 642 } 643 } 644 for (String value : values) { 645 builder.put(normalizedAttribute, normalizeParameterValue(normalizedAttribute, value)); 646 } 647 MediaType mediaType = new MediaType(type, subtype, builder.build()); 648 // if the attribute isn't charset, we can just inherit the current parsedCharset 649 if (!normalizedAttribute.equals(CHARSET_ATTRIBUTE)) { 650 mediaType.parsedCharset = this.parsedCharset; 651 } 652 // Return one of the constants if the media type is a known type. 653 return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); 654 } 655 656 /** 657 * <em>Replaces</em> all parameters with the given attribute with a single parameter with the 658 * given value. If multiple parameters with the same attributes are necessary use {@link 659 * #withParameters(String, Iterable)}. Prefer {@link #withCharset} for setting the {@code charset} 660 * parameter when using a {@link Charset} object. 661 * 662 * @throws IllegalArgumentException if either {@code attribute} or {@code value} is invalid 663 */ 664 public MediaType withParameter(String attribute, String value) { 665 return withParameters(attribute, ImmutableSet.of(value)); 666 } 667 668 /** 669 * Returns a new instance with the same type and subtype as this instance, with the {@code 670 * charset} parameter set to the {@link Charset#name name} of the given charset. Only one {@code 671 * charset} parameter will be present on the new instance regardless of the number set on this 672 * one. 673 * 674 * <p>If a charset must be specified that is not supported on this JVM (and thus is not 675 * representable as a {@link Charset} instance, use {@link #withParameter}. 676 */ 677 public MediaType withCharset(Charset charset) { 678 checkNotNull(charset); 679 MediaType withCharset = withParameter(CHARSET_ATTRIBUTE, charset.name()); 680 // precache the charset so we don't need to parse it 681 withCharset.parsedCharset = Optional.of(charset); 682 return withCharset; 683 } 684 685 /** Returns true if either the type or subtype is the wildcard. */ 686 public boolean hasWildcard() { 687 return WILDCARD.equals(type) || WILDCARD.equals(subtype); 688 } 689 690 /** 691 * Returns {@code true} if this instance falls within the range (as defined by <a 692 * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">the HTTP Accept header</a>) given 693 * by the argument according to three criteria: 694 * 695 * <ol> 696 * <li>The type of the argument is the wildcard or equal to the type of this instance. 697 * <li>The subtype of the argument is the wildcard or equal to the subtype of this instance. 698 * <li>All of the parameters present in the argument are present in this instance. 699 * </ol> 700 * 701 * <p>For example: 702 * 703 * <pre>{@code 704 * PLAIN_TEXT_UTF_8.is(PLAIN_TEXT_UTF_8) // true 705 * PLAIN_TEXT_UTF_8.is(HTML_UTF_8) // false 706 * PLAIN_TEXT_UTF_8.is(ANY_TYPE) // true 707 * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE) // true 708 * PLAIN_TEXT_UTF_8.is(ANY_IMAGE_TYPE) // false 709 * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_8)) // true 710 * PLAIN_TEXT_UTF_8.withoutParameters().is(ANY_TEXT_TYPE.withCharset(UTF_8)) // false 711 * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_16)) // false 712 * }</pre> 713 * 714 * <p>Note that while it is possible to have the same parameter declared multiple times within a 715 * media type this method does not consider the number of occurrences of a parameter. For example, 716 * {@code "text/plain; charset=UTF-8"} satisfies {@code "text/plain; charset=UTF-8; 717 * charset=UTF-8"}. 718 */ 719 public boolean is(MediaType mediaTypeRange) { 720 return (mediaTypeRange.type.equals(WILDCARD) || mediaTypeRange.type.equals(this.type)) 721 && (mediaTypeRange.subtype.equals(WILDCARD) || mediaTypeRange.subtype.equals(this.subtype)) 722 && this.parameters.entries().containsAll(mediaTypeRange.parameters.entries()); 723 } 724 725 /** 726 * Creates a new media type with the given type and subtype. 727 * 728 * @throws IllegalArgumentException if type or subtype is invalid or if a wildcard is used for the 729 * type, but not the subtype. 730 */ 731 public static MediaType create(String type, String subtype) { 732 MediaType mediaType = create(type, subtype, ImmutableListMultimap.<String, String>of()); 733 mediaType.parsedCharset = Optional.absent(); 734 return mediaType; 735 } 736 737 /** 738 * Creates a media type with the "application" type and the given subtype. 739 * 740 * @throws IllegalArgumentException if subtype is invalid 741 */ 742 static MediaType createApplicationType(String subtype) { 743 return create(APPLICATION_TYPE, subtype); 744 } 745 746 /** 747 * Creates a media type with the "audio" type and the given subtype. 748 * 749 * @throws IllegalArgumentException if subtype is invalid 750 */ 751 static MediaType createAudioType(String subtype) { 752 return create(AUDIO_TYPE, subtype); 753 } 754 755 /** 756 * Creates a media type with the "image" type and the given subtype. 757 * 758 * @throws IllegalArgumentException if subtype is invalid 759 */ 760 static MediaType createImageType(String subtype) { 761 return create(IMAGE_TYPE, subtype); 762 } 763 764 /** 765 * Creates a media type with the "text" type and the given subtype. 766 * 767 * @throws IllegalArgumentException if subtype is invalid 768 */ 769 static MediaType createTextType(String subtype) { 770 return create(TEXT_TYPE, subtype); 771 } 772 773 /** 774 * Creates a media type with the "video" type and the given subtype. 775 * 776 * @throws IllegalArgumentException if subtype is invalid 777 */ 778 static MediaType createVideoType(String subtype) { 779 return create(VIDEO_TYPE, subtype); 780 } 781 782 private static MediaType create( 783 String type, String subtype, Multimap<String, String> parameters) { 784 checkNotNull(type); 785 checkNotNull(subtype); 786 checkNotNull(parameters); 787 String normalizedType = normalizeToken(type); 788 String normalizedSubtype = normalizeToken(subtype); 789 checkArgument( 790 !WILDCARD.equals(normalizedType) || WILDCARD.equals(normalizedSubtype), 791 "A wildcard type cannot be used with a non-wildcard subtype"); 792 ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder(); 793 for (Entry<String, String> entry : parameters.entries()) { 794 String attribute = normalizeToken(entry.getKey()); 795 builder.put(attribute, normalizeParameterValue(attribute, entry.getValue())); 796 } 797 MediaType mediaType = new MediaType(normalizedType, normalizedSubtype, builder.build()); 798 // Return one of the constants if the media type is a known type. 799 return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); 800 } 801 802 private static String normalizeToken(String token) { 803 checkArgument(TOKEN_MATCHER.matchesAllOf(token)); 804 return Ascii.toLowerCase(token); 805 } 806 807 private static String normalizeParameterValue(String attribute, String value) { 808 return CHARSET_ATTRIBUTE.equals(attribute) ? Ascii.toLowerCase(value) : value; 809 } 810 811 /** 812 * Parses a media type from its string representation. 813 * 814 * @throws IllegalArgumentException if the input is not parsable 815 */ 816 public static MediaType parse(String input) { 817 checkNotNull(input); 818 Tokenizer tokenizer = new Tokenizer(input); 819 try { 820 String type = tokenizer.consumeToken(TOKEN_MATCHER); 821 tokenizer.consumeCharacter('/'); 822 String subtype = tokenizer.consumeToken(TOKEN_MATCHER); 823 ImmutableListMultimap.Builder<String, String> parameters = ImmutableListMultimap.builder(); 824 while (tokenizer.hasMore()) { 825 tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); 826 tokenizer.consumeCharacter(';'); 827 tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); 828 String attribute = tokenizer.consumeToken(TOKEN_MATCHER); 829 tokenizer.consumeCharacter('='); 830 final String value; 831 if ('"' == tokenizer.previewChar()) { 832 tokenizer.consumeCharacter('"'); 833 StringBuilder valueBuilder = new StringBuilder(); 834 while ('"' != tokenizer.previewChar()) { 835 if ('\\' == tokenizer.previewChar()) { 836 tokenizer.consumeCharacter('\\'); 837 valueBuilder.append(tokenizer.consumeCharacter(ascii())); 838 } else { 839 valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER)); 840 } 841 } 842 value = valueBuilder.toString(); 843 tokenizer.consumeCharacter('"'); 844 } else { 845 value = tokenizer.consumeToken(TOKEN_MATCHER); 846 } 847 parameters.put(attribute, value); 848 } 849 return create(type, subtype, parameters.build()); 850 } catch (IllegalStateException e) { 851 throw new IllegalArgumentException("Could not parse '" + input + "'", e); 852 } 853 } 854 855 private static final class Tokenizer { 856 final String input; 857 int position = 0; 858 859 Tokenizer(String input) { 860 this.input = input; 861 } 862 863 String consumeTokenIfPresent(CharMatcher matcher) { 864 checkState(hasMore()); 865 int startPosition = position; 866 position = matcher.negate().indexIn(input, startPosition); 867 return hasMore() ? input.substring(startPosition, position) : input.substring(startPosition); 868 } 869 870 String consumeToken(CharMatcher matcher) { 871 int startPosition = position; 872 String token = consumeTokenIfPresent(matcher); 873 checkState(position != startPosition); 874 return token; 875 } 876 877 char consumeCharacter(CharMatcher matcher) { 878 checkState(hasMore()); 879 char c = previewChar(); 880 checkState(matcher.matches(c)); 881 position++; 882 return c; 883 } 884 885 char consumeCharacter(char c) { 886 checkState(hasMore()); 887 checkState(previewChar() == c); 888 position++; 889 return c; 890 } 891 892 char previewChar() { 893 checkState(hasMore()); 894 return input.charAt(position); 895 } 896 897 boolean hasMore() { 898 return (position >= 0) && (position < input.length()); 899 } 900 } 901 902 @Override 903 public boolean equals(@NullableDecl Object obj) { 904 if (obj == this) { 905 return true; 906 } else if (obj instanceof MediaType) { 907 MediaType that = (MediaType) obj; 908 return this.type.equals(that.type) 909 && this.subtype.equals(that.subtype) 910 // compare parameters regardless of order 911 && this.parametersAsMap().equals(that.parametersAsMap()); 912 } else { 913 return false; 914 } 915 } 916 917 @Override 918 public int hashCode() { 919 // racy single-check idiom 920 int h = hashCode; 921 if (h == 0) { 922 h = Objects.hashCode(type, subtype, parametersAsMap()); 923 hashCode = h; 924 } 925 return h; 926 } 927 928 private static final MapJoiner PARAMETER_JOINER = Joiner.on("; ").withKeyValueSeparator("="); 929 930 /** 931 * Returns the string representation of this media type in the format described in <a 932 * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>. 933 */ 934 @Override 935 public String toString() { 936 // racy single-check idiom, safe because String is immutable 937 String result = toString; 938 if (result == null) { 939 result = computeToString(); 940 toString = result; 941 } 942 return result; 943 } 944 945 private String computeToString() { 946 StringBuilder builder = new StringBuilder().append(type).append('/').append(subtype); 947 if (!parameters.isEmpty()) { 948 builder.append("; "); 949 Multimap<String, String> quotedParameters = 950 Multimaps.transformValues( 951 parameters, 952 new Function<String, String>() { 953 @Override 954 public String apply(String value) { 955 return TOKEN_MATCHER.matchesAllOf(value) ? value : escapeAndQuote(value); 956 } 957 }); 958 PARAMETER_JOINER.appendTo(builder, quotedParameters.entries()); 959 } 960 return builder.toString(); 961 } 962 963 private static String escapeAndQuote(String value) { 964 StringBuilder escaped = new StringBuilder(value.length() + 16).append('"'); 965 for (int i = 0; i < value.length(); i++) { 966 char ch = value.charAt(i); 967 if (ch == '\r' || ch == '\\' || ch == '"') { 968 escaped.append('\\'); 969 } 970 escaped.append(ch); 971 } 972 return escaped.append('"').toString(); 973 } 974}