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