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