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