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 442 public static final MediaType OGG_CONTAINER = createConstant(APPLICATION_TYPE, "ogg"); 443 public static final MediaType OOXML_DOCUMENT = 444 createConstant( 445 APPLICATION_TYPE, "vnd.openxmlformats-officedocument.wordprocessingml.document"); 446 public static final MediaType OOXML_PRESENTATION = 447 createConstant( 448 APPLICATION_TYPE, "vnd.openxmlformats-officedocument.presentationml.presentation"); 449 public static final MediaType OOXML_SHEET = 450 createConstant(APPLICATION_TYPE, "vnd.openxmlformats-officedocument.spreadsheetml.sheet"); 451 public static final MediaType OPENDOCUMENT_GRAPHICS = 452 createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.graphics"); 453 public static final MediaType OPENDOCUMENT_PRESENTATION = 454 createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.presentation"); 455 public static final MediaType OPENDOCUMENT_SPREADSHEET = 456 createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.spreadsheet"); 457 public static final MediaType OPENDOCUMENT_TEXT = 458 createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.text"); 459 public static final MediaType PDF = createConstant(APPLICATION_TYPE, "pdf"); 460 public static final MediaType POSTSCRIPT = createConstant(APPLICATION_TYPE, "postscript"); 461 /** 462 * <a href="http://tools.ietf.org/html/draft-rfernando-protocol-buffers-00">Protocol buffers</a> 463 * 464 * @since 15.0 465 */ 466 public static final MediaType PROTOBUF = createConstant(APPLICATION_TYPE, "protobuf"); 467 468 public static final MediaType RDF_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rdf+xml"); 469 public static final MediaType RTF_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rtf"); 470 /** 471 * Media type for SFNT fonts (which includes 472 * <a href="http://en.wikipedia.org/wiki/TrueType/">TrueType</a> and 473 * <a href="http://en.wikipedia.org/wiki/OpenType/">OpenType</a> fonts). This is 474 * <a href="http://www.iana.org/assignments/media-types/application/font-sfnt">registered</a> with 475 * the IANA. 476 * 477 * @since 17.0 478 */ 479 public static final MediaType SFNT = createConstant(APPLICATION_TYPE, "font-sfnt"); 480 public static final MediaType SHOCKWAVE_FLASH = 481 createConstant(APPLICATION_TYPE, "x-shockwave-flash"); 482 public static final MediaType SKETCHUP = createConstant(APPLICATION_TYPE, "vnd.sketchup.skp"); 483 /** 484 * As described in <a href="http://www.ietf.org/rfc/rfc3902.txt">RFC 3902</a>, this constant 485 * ({@code application/soap+xml}) is used to identify SOAP 1.2 message envelopes that have been 486 * serialized with XML 1.0. 487 * 488 * <p>For SOAP 1.1 messages, see {@code XML_UTF_8} per <a 489 * href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/">W3C Note on Simple Object Access Protocol 490 * (SOAP) 1.1</a> 491 * 492 * @since 20.0 493 */ 494 public static final MediaType SOAP_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "soap+xml"); 495 public static final MediaType TAR = createConstant(APPLICATION_TYPE, "x-tar"); 496 /** 497 * Media type for the <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font 498 * Format</a> (WOFF) <a href="http://www.w3.org/TR/WOFF/">defined</a> by the W3C. This is 499 * <a href="http://www.iana.org/assignments/media-types/application/font-woff">registered</a> with 500 * the IANA. 501 * 502 * @since 17.0 503 */ 504 public static final MediaType WOFF = createConstant(APPLICATION_TYPE, "font-woff"); 505 /** 506 * Media type for the <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font 507 * Format</a> (WOFF) version 2 <a href="https://www.w3.org/TR/WOFF2/">defined</a> by the W3C. 508 * 509 * @since 20.0 510 */ 511 public static final MediaType WOFF2 = createConstant(APPLICATION_TYPE, "font-woff2"); 512 public static final MediaType XHTML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xhtml+xml"); 513 /** 514 * Media type for Extensible Resource Descriptors. This is not yet registered with the IANA, but 515 * it is specified by OASIS in the 516 * <a href="http://docs.oasis-open.org/xri/xrd/v1.0/cd02/xrd-1.0-cd02.html">XRD definition</a> and 517 * implemented in projects such as <a href="http://code.google.com/p/webfinger/">WebFinger</a>. 518 */ 519 public static final MediaType XRD_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xrd+xml"); 520 public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip"); 521 522 private final String type; 523 private final String subtype; 524 private final ImmutableListMultimap<String, String> parameters; 525 526 @LazyInit 527 private String toString; 528 529 @LazyInit 530 private int hashCode; 531 532 private MediaType(String type, String subtype, ImmutableListMultimap<String, String> parameters) { 533 this.type = type; 534 this.subtype = subtype; 535 this.parameters = parameters; 536 } 537 538 /** Returns the top-level media type. For example, {@code "text"} in {@code "text/plain"}. */ 539 public String type() { 540 return type; 541 } 542 543 /** Returns the media subtype. For example, {@code "plain"} in {@code "text/plain"}. */ 544 public String subtype() { 545 return subtype; 546 } 547 548 /** Returns a multimap containing the parameters of this media type. */ 549 public ImmutableListMultimap<String, String> parameters() { 550 return parameters; 551 } 552 553 private Map<String, ImmutableMultiset<String>> parametersAsMap() { 554 return Maps.transformValues( 555 parameters.asMap(), 556 new Function<Collection<String>, ImmutableMultiset<String>>() { 557 @Override 558 public ImmutableMultiset<String> apply(Collection<String> input) { 559 return ImmutableMultiset.copyOf(input); 560 } 561 }); 562 } 563 564 /** 565 * Returns an optional charset for the value of the charset parameter if it is specified. 566 * 567 * @throws IllegalStateException if multiple charset values have been set for this media type 568 * @throws IllegalCharsetNameException if a charset value is present, but illegal 569 * @throws UnsupportedCharsetException if a charset value is present, but no support is available 570 * in this instance of the Java virtual machine 571 */ 572 public Optional<Charset> charset() { 573 ImmutableSet<String> charsetValues = ImmutableSet.copyOf(parameters.get(CHARSET_ATTRIBUTE)); 574 switch (charsetValues.size()) { 575 case 0: 576 return Optional.absent(); 577 case 1: 578 return Optional.of(Charset.forName(Iterables.getOnlyElement(charsetValues))); 579 default: 580 throw new IllegalStateException("Multiple charset values defined: " + charsetValues); 581 } 582 } 583 584 /** 585 * Returns a new instance with the same type and subtype as this instance, but without any 586 * parameters. 587 */ 588 public MediaType withoutParameters() { 589 return parameters.isEmpty() ? this : create(type, subtype); 590 } 591 592 /** 593 * <em>Replaces</em> all parameters with the given parameters. 594 * 595 * @throws IllegalArgumentException if any parameter or value is invalid 596 */ 597 public MediaType withParameters(Multimap<String, String> parameters) { 598 return create(type, subtype, parameters); 599 } 600 601 /** 602 * <em>Replaces</em> all parameters with the given attribute with a single parameter with the 603 * given value. If multiple parameters with the same attributes are necessary use 604 * {@link #withParameters}. Prefer {@link #withCharset} for setting the {@code charset} parameter 605 * when using a {@link Charset} object. 606 * 607 * @throws IllegalArgumentException if either {@code attribute} or {@code value} is invalid 608 */ 609 public MediaType withParameter(String attribute, String value) { 610 checkNotNull(attribute); 611 checkNotNull(value); 612 String normalizedAttribute = normalizeToken(attribute); 613 ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder(); 614 for (Entry<String, String> entry : parameters.entries()) { 615 String key = entry.getKey(); 616 if (!normalizedAttribute.equals(key)) { 617 builder.put(key, entry.getValue()); 618 } 619 } 620 builder.put(normalizedAttribute, normalizeParameterValue(normalizedAttribute, value)); 621 MediaType mediaType = new MediaType(type, subtype, builder.build()); 622 // Return one of the constants if the media type is a known type. 623 return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); 624 } 625 626 /** 627 * Returns a new instance with the same type and subtype as this instance, with the 628 * {@code charset} parameter set to the {@link Charset#name name} of the given charset. Only one 629 * {@code charset} parameter will be present on the new instance regardless of the number set on 630 * this one. 631 * 632 * <p>If a charset must be specified that is not supported on this JVM (and thus is not 633 * representable as a {@link Charset} instance, use {@link #withParameter}. 634 */ 635 public MediaType withCharset(Charset charset) { 636 checkNotNull(charset); 637 return withParameter(CHARSET_ATTRIBUTE, charset.name()); 638 } 639 640 /** Returns true if either the type or subtype is the wildcard. */ 641 public boolean hasWildcard() { 642 return WILDCARD.equals(type) || WILDCARD.equals(subtype); 643 } 644 645 /** 646 * Returns {@code true} if this instance falls within the range (as defined by 647 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">the HTTP Accept header</a>) 648 * given by the argument according to three criteria: 649 * 650 * <ol> 651 * <li>The type of the argument is the wildcard or equal to the type of this instance. 652 * <li>The subtype of the argument is the wildcard or equal to the subtype of this instance. 653 * <li>All of the parameters present in the argument are present in this instance. 654 * </ol> 655 * 656 * <p>For example: <pre> {@code 657 * PLAIN_TEXT_UTF_8.is(PLAIN_TEXT_UTF_8) // true 658 * PLAIN_TEXT_UTF_8.is(HTML_UTF_8) // false 659 * PLAIN_TEXT_UTF_8.is(ANY_TYPE) // true 660 * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE) // true 661 * PLAIN_TEXT_UTF_8.is(ANY_IMAGE_TYPE) // false 662 * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_8)) // true 663 * PLAIN_TEXT_UTF_8.withoutParameters().is(ANY_TEXT_TYPE.withCharset(UTF_8)) // false 664 * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_16)) // false}</pre> 665 * 666 * <p>Note that while it is possible to have the same parameter declared multiple times within a 667 * media type this method does not consider the number of occurrences of a parameter. For example, 668 * {@code "text/plain; charset=UTF-8"} satisfies 669 * {@code "text/plain; charset=UTF-8; charset=UTF-8"}. 670 */ 671 public boolean is(MediaType mediaTypeRange) { 672 return (mediaTypeRange.type.equals(WILDCARD) || mediaTypeRange.type.equals(this.type)) 673 && (mediaTypeRange.subtype.equals(WILDCARD) || mediaTypeRange.subtype.equals(this.subtype)) 674 && this.parameters.entries().containsAll(mediaTypeRange.parameters.entries()); 675 } 676 677 /** 678 * Creates a new media type with the given type and subtype. 679 * 680 * @throws IllegalArgumentException if type or subtype is invalid or if a wildcard is used for the 681 * type, but not the subtype. 682 */ 683 public static MediaType create(String type, String subtype) { 684 return create(type, subtype, ImmutableListMultimap.<String, String>of()); 685 } 686 687 /** 688 * Creates a media type with the "application" type and the given subtype. 689 * 690 * @throws IllegalArgumentException if subtype is invalid 691 */ 692 static MediaType createApplicationType(String subtype) { 693 return create(APPLICATION_TYPE, subtype); 694 } 695 696 /** 697 * Creates a media type with the "audio" type and the given subtype. 698 * 699 * @throws IllegalArgumentException if subtype is invalid 700 */ 701 static MediaType createAudioType(String subtype) { 702 return create(AUDIO_TYPE, subtype); 703 } 704 705 /** 706 * Creates a media type with the "image" type and the given subtype. 707 * 708 * @throws IllegalArgumentException if subtype is invalid 709 */ 710 static MediaType createImageType(String subtype) { 711 return create(IMAGE_TYPE, subtype); 712 } 713 714 /** 715 * Creates a media type with the "text" type and the given subtype. 716 * 717 * @throws IllegalArgumentException if subtype is invalid 718 */ 719 static MediaType createTextType(String subtype) { 720 return create(TEXT_TYPE, subtype); 721 } 722 723 /** 724 * Creates a media type with the "video" type and the given subtype. 725 * 726 * @throws IllegalArgumentException if subtype is invalid 727 */ 728 static MediaType createVideoType(String subtype) { 729 return create(VIDEO_TYPE, subtype); 730 } 731 732 private static MediaType create( 733 String type, String subtype, Multimap<String, String> parameters) { 734 checkNotNull(type); 735 checkNotNull(subtype); 736 checkNotNull(parameters); 737 String normalizedType = normalizeToken(type); 738 String normalizedSubtype = normalizeToken(subtype); 739 checkArgument( 740 !WILDCARD.equals(normalizedType) || WILDCARD.equals(normalizedSubtype), 741 "A wildcard type cannot be used with a non-wildcard subtype"); 742 ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder(); 743 for (Entry<String, String> entry : parameters.entries()) { 744 String attribute = normalizeToken(entry.getKey()); 745 builder.put(attribute, normalizeParameterValue(attribute, entry.getValue())); 746 } 747 MediaType mediaType = new MediaType(normalizedType, normalizedSubtype, builder.build()); 748 // Return one of the constants if the media type is a known type. 749 return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); 750 } 751 752 private static String normalizeToken(String token) { 753 checkArgument(TOKEN_MATCHER.matchesAllOf(token)); 754 return Ascii.toLowerCase(token); 755 } 756 757 private static String normalizeParameterValue(String attribute, String value) { 758 return CHARSET_ATTRIBUTE.equals(attribute) ? Ascii.toLowerCase(value) : value; 759 } 760 761 /** 762 * Parses a media type from its string representation. 763 * 764 * @throws IllegalArgumentException if the input is not parsable 765 */ 766 public static MediaType parse(String input) { 767 checkNotNull(input); 768 Tokenizer tokenizer = new Tokenizer(input); 769 try { 770 String type = tokenizer.consumeToken(TOKEN_MATCHER); 771 tokenizer.consumeCharacter('/'); 772 String subtype = tokenizer.consumeToken(TOKEN_MATCHER); 773 ImmutableListMultimap.Builder<String, String> parameters = ImmutableListMultimap.builder(); 774 while (tokenizer.hasMore()) { 775 tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); 776 tokenizer.consumeCharacter(';'); 777 tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); 778 String attribute = tokenizer.consumeToken(TOKEN_MATCHER); 779 tokenizer.consumeCharacter('='); 780 final String value; 781 if ('"' == tokenizer.previewChar()) { 782 tokenizer.consumeCharacter('"'); 783 StringBuilder valueBuilder = new StringBuilder(); 784 while ('"' != tokenizer.previewChar()) { 785 if ('\\' == tokenizer.previewChar()) { 786 tokenizer.consumeCharacter('\\'); 787 valueBuilder.append(tokenizer.consumeCharacter(ascii())); 788 } else { 789 valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER)); 790 } 791 } 792 value = valueBuilder.toString(); 793 tokenizer.consumeCharacter('"'); 794 } else { 795 value = tokenizer.consumeToken(TOKEN_MATCHER); 796 } 797 parameters.put(attribute, value); 798 } 799 return create(type, subtype, parameters.build()); 800 } catch (IllegalStateException e) { 801 throw new IllegalArgumentException("Could not parse '" + input + "'", e); 802 } 803 } 804 805 private static final class Tokenizer { 806 final String input; 807 int position = 0; 808 809 Tokenizer(String input) { 810 this.input = input; 811 } 812 813 String consumeTokenIfPresent(CharMatcher matcher) { 814 checkState(hasMore()); 815 int startPosition = position; 816 position = matcher.negate().indexIn(input, startPosition); 817 return hasMore() ? input.substring(startPosition, position) : input.substring(startPosition); 818 } 819 820 String consumeToken(CharMatcher matcher) { 821 int startPosition = position; 822 String token = consumeTokenIfPresent(matcher); 823 checkState(position != startPosition); 824 return token; 825 } 826 827 char consumeCharacter(CharMatcher matcher) { 828 checkState(hasMore()); 829 char c = previewChar(); 830 checkState(matcher.matches(c)); 831 position++; 832 return c; 833 } 834 835 char consumeCharacter(char c) { 836 checkState(hasMore()); 837 checkState(previewChar() == c); 838 position++; 839 return c; 840 } 841 842 char previewChar() { 843 checkState(hasMore()); 844 return input.charAt(position); 845 } 846 847 boolean hasMore() { 848 return (position >= 0) && (position < input.length()); 849 } 850 } 851 852 @Override 853 public boolean equals(@Nullable Object obj) { 854 if (obj == this) { 855 return true; 856 } else if (obj instanceof MediaType) { 857 MediaType that = (MediaType) obj; 858 return this.type.equals(that.type) 859 && this.subtype.equals(that.subtype) 860 // compare parameters regardless of order 861 && this.parametersAsMap().equals(that.parametersAsMap()); 862 } else { 863 return false; 864 } 865 } 866 867 @Override 868 public int hashCode() { 869 // racy single-check idiom 870 int h = hashCode; 871 if (h == 0) { 872 h = Objects.hashCode(type, subtype, parametersAsMap()); 873 hashCode = h; 874 } 875 return h; 876 } 877 878 private static final MapJoiner PARAMETER_JOINER = Joiner.on("; ").withKeyValueSeparator("="); 879 880 /** 881 * Returns the string representation of this media type in the format described in 882 * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>. 883 */ 884 @Override 885 public String toString() { 886 // racy single-check idiom, safe because String is immutable 887 String result = toString; 888 if (result == null) { 889 result = computeToString(); 890 toString = result; 891 } 892 return result; 893 } 894 895 private String computeToString() { 896 StringBuilder builder = new StringBuilder().append(type).append('/').append(subtype); 897 if (!parameters.isEmpty()) { 898 builder.append("; "); 899 Multimap<String, String> quotedParameters = 900 Multimaps.transformValues( 901 parameters, 902 new Function<String, String>() { 903 @Override 904 public String apply(String value) { 905 return TOKEN_MATCHER.matchesAllOf(value) ? value : escapeAndQuote(value); 906 } 907 }); 908 PARAMETER_JOINER.appendTo(builder, quotedParameters.entries()); 909 } 910 return builder.toString(); 911 } 912 913 private static String escapeAndQuote(String value) { 914 StringBuilder escaped = new StringBuilder(value.length() + 16).append('"'); 915 for (int i = 0; i < value.length(); i++) { 916 char ch = value.charAt(i); 917 if (ch == '\r' || ch == '\\' || ch == '"') { 918 escaped.append('\\'); 919 } 920 escaped.append(ch); 921 } 922 return escaped.append('"').toString(); 923 } 924}