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