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