All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.net.MediaType Maven / Gradle / Ivy

Go to download

Guava is a suite of core and expanded libraries that include utility classes, google's collections, io classes, and much much more.

There is a newer version: 33.1.0-jre
Show newest version
/*
 * Copyright (C) 2011 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.net;

import static com.google.common.base.CharMatcher.ascii;
import static com.google.common.base.CharMatcher.javaIsoControl;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Ascii;
import com.google.common.base.CharMatcher;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Joiner.MapJoiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.annotations.concurrent.LazyInit;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Represents an Internet Media Type
 * (also known as a MIME Type or Content Type). This class also supports the concept of media ranges
 * defined by HTTP/1.1.
 * As such, the {@code *} character is treated as a wildcard and is used to represent any acceptable
 * type or subtype value. A media type may not have wildcard type with a declared subtype. The
 * {@code *} character has no special meaning as part of a parameter. All values for type, subtype,
 * parameter attributes or parameter values must be valid according to RFCs 2045 and 2046.
 *
 * 

All portions of the media type that are case-insensitive (type, subtype, parameter attributes) * are normalized to lowercase. The value of the {@code charset} parameter is normalized to * lowercase, but all others are left as-is. * *

Note that this specifically does not represent the value of the MIME {@code * Content-Type} header and as such has no support for header-specific considerations such as line * folding and comments. * *

For media types that take a charset the predefined constants default to UTF-8 and have a * "_UTF_8" suffix. To get a version without a character set, use {@link #withoutParameters}. * * @since 12.0 * @author Gregory Kick */ @Beta @GwtCompatible @Immutable public final class MediaType { private static final String CHARSET_ATTRIBUTE = "charset"; private static final ImmutableListMultimap UTF_8_CONSTANT_PARAMETERS = ImmutableListMultimap.of(CHARSET_ATTRIBUTE, Ascii.toLowerCase(UTF_8.name())); /** Matcher for type, subtype and attributes. */ private static final CharMatcher TOKEN_MATCHER = ascii() .and(javaIsoControl().negate()) .and(CharMatcher.isNot(' ')) .and(CharMatcher.noneOf("()<>@,;:\\\"/[]?=")); private static final CharMatcher QUOTED_TEXT_MATCHER = ascii().and(CharMatcher.noneOf("\"\\\r")); /* * This matches the same characters as linear-white-space from RFC 822, but we make no effort to * enforce any particular rules with regards to line folding as stated in the class docs. */ private static final CharMatcher LINEAR_WHITE_SPACE = CharMatcher.anyOf(" \t\r\n"); // TODO(gak): make these public? private static final String APPLICATION_TYPE = "application"; private static final String AUDIO_TYPE = "audio"; private static final String IMAGE_TYPE = "image"; private static final String TEXT_TYPE = "text"; private static final String VIDEO_TYPE = "video"; private static final String WILDCARD = "*"; private static final Map KNOWN_TYPES = Maps.newHashMap(); private static MediaType createConstant(String type, String subtype) { MediaType mediaType = addKnownType(new MediaType(type, subtype, ImmutableListMultimap.of())); mediaType.parsedCharset = Optional.absent(); return mediaType; } private static MediaType createConstantUtf8(String type, String subtype) { MediaType mediaType = addKnownType(new MediaType(type, subtype, UTF_8_CONSTANT_PARAMETERS)); mediaType.parsedCharset = Optional.of(UTF_8); return mediaType; } private static MediaType addKnownType(MediaType mediaType) { KNOWN_TYPES.put(mediaType, mediaType); return mediaType; } /* * The following constants are grouped by their type and ordered alphabetically by the constant * name within that type. The constant name should be a sensible identifier that is closest to the * "common name" of the media. This is often, but not necessarily the same as the subtype. * * Be sure to declare all constants with the type and subtype in all lowercase. For types that * take a charset (e.g. all text/* types), default to UTF-8 and suffix the constant name with * "_UTF_8". */ public static final MediaType ANY_TYPE = createConstant(WILDCARD, WILDCARD); public static final MediaType ANY_TEXT_TYPE = createConstant(TEXT_TYPE, WILDCARD); public static final MediaType ANY_IMAGE_TYPE = createConstant(IMAGE_TYPE, WILDCARD); public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD); public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD); public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD); /* text types */ public static final MediaType CACHE_MANIFEST_UTF_8 = createConstantUtf8(TEXT_TYPE, "cache-manifest"); public static final MediaType CSS_UTF_8 = createConstantUtf8(TEXT_TYPE, "css"); public static final MediaType CSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "csv"); public static final MediaType HTML_UTF_8 = createConstantUtf8(TEXT_TYPE, "html"); public static final MediaType I_CALENDAR_UTF_8 = createConstantUtf8(TEXT_TYPE, "calendar"); public static final MediaType PLAIN_TEXT_UTF_8 = createConstantUtf8(TEXT_TYPE, "plain"); /** * RFC 4329 declares {@link * #JAVASCRIPT_UTF_8 application/javascript} to be the correct media type for JavaScript, but this * may be necessary in certain situations for compatibility. */ public static final MediaType TEXT_JAVASCRIPT_UTF_8 = createConstantUtf8(TEXT_TYPE, "javascript"); /** * Tab separated * values. * * @since 15.0 */ public static final MediaType TSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "tab-separated-values"); public static final MediaType VCARD_UTF_8 = createConstantUtf8(TEXT_TYPE, "vcard"); /** * UTF-8 encoded Wireless Markup * Language. * * @since 13.0 */ public static final MediaType WML_UTF_8 = createConstantUtf8(TEXT_TYPE, "vnd.wap.wml"); /** * As described in RFC 3023, this constant * ({@code text/xml}) is used for XML documents that are "readable by casual users." {@link * #APPLICATION_XML_UTF_8} is provided for documents that are intended for applications. */ public static final MediaType XML_UTF_8 = createConstantUtf8(TEXT_TYPE, "xml"); /** * As described in the VTT spec, this is * used for Web Video Text Tracks (WebVTT) files, used with the HTML5 track element. * * @since 20.0 */ public static final MediaType VTT_UTF_8 = createConstantUtf8(TEXT_TYPE, "vtt"); /** * Bitmap file format ({@code bmp} * files). * * @since 13.0 */ public static final MediaType BMP = createConstant(IMAGE_TYPE, "bmp"); /** * The Canon Image File * Format ({@code crw} files), a widely-used "raw image" format for cameras. It is found in * {@code /etc/mime.types}, e.g. in Debian 3.48-1. * * @since 15.0 */ public static final MediaType CRW = createConstant(IMAGE_TYPE, "x-canon-crw"); public static final MediaType GIF = createConstant(IMAGE_TYPE, "gif"); public static final MediaType ICO = createConstant(IMAGE_TYPE, "vnd.microsoft.icon"); public static final MediaType JPEG = createConstant(IMAGE_TYPE, "jpeg"); public static final MediaType PNG = createConstant(IMAGE_TYPE, "png"); /** * The Photoshop File Format ({@code psd} files) as defined by IANA, and * found in {@code /etc/mime.types}, e.g. of the * Apache HTTPD project; for the specification, see * Adobe Photoshop Document Format and Wikipedia; this is the * regular output/input of Photoshop (which can also export to various image formats; note that * files with extension "PSB" are in a distinct but related format). * *

This is a more recent replacement for the older, experimental type {@code x-photoshop}: RFC-2046.6. * * @since 15.0 */ public static final MediaType PSD = createConstant(IMAGE_TYPE, "vnd.adobe.photoshop"); public static final MediaType SVG_UTF_8 = createConstantUtf8(IMAGE_TYPE, "svg+xml"); public static final MediaType TIFF = createConstant(IMAGE_TYPE, "tiff"); /** * WebP image format. * * @since 13.0 */ public static final MediaType WEBP = createConstant(IMAGE_TYPE, "webp"); /* audio types */ public static final MediaType MP4_AUDIO = createConstant(AUDIO_TYPE, "mp4"); public static final MediaType MPEG_AUDIO = createConstant(AUDIO_TYPE, "mpeg"); public static final MediaType OGG_AUDIO = createConstant(AUDIO_TYPE, "ogg"); public static final MediaType WEBM_AUDIO = createConstant(AUDIO_TYPE, "webm"); /** * L16 audio, as defined by RFC 2586. * * @since 24.1 */ public static final MediaType L16_AUDIO = createConstant(AUDIO_TYPE, "l16"); /** * L24 audio, as defined by RFC 3190. * * @since 20.0 */ public static final MediaType L24_AUDIO = createConstant(AUDIO_TYPE, "l24"); /** * Basic Audio, as defined by RFC * 2046. * * @since 20.0 */ public static final MediaType BASIC_AUDIO = createConstant(AUDIO_TYPE, "basic"); /** * Advanced Audio Coding. For more information, see Advanced Audio Coding. * * @since 20.0 */ public static final MediaType AAC_AUDIO = createConstant(AUDIO_TYPE, "aac"); /** * Vorbis Audio, as defined by RFC 5215. * * @since 20.0 */ public static final MediaType VORBIS_AUDIO = createConstant(AUDIO_TYPE, "vorbis"); /** * Windows Media Audio. For more information, see file * name extensions for Windows Media metafiles. * * @since 20.0 */ public static final MediaType WMA_AUDIO = createConstant(AUDIO_TYPE, "x-ms-wma"); /** * Windows Media metafiles. For more information, see file * name extensions for Windows Media metafiles. * * @since 20.0 */ public static final MediaType WAX_AUDIO = createConstant(AUDIO_TYPE, "x-ms-wax"); /** * Real Audio. For more information, see this link. * * @since 20.0 */ public static final MediaType VND_REAL_AUDIO = createConstant(AUDIO_TYPE, "vnd.rn-realaudio"); /** * WAVE format, as defined by RFC 2361. * * @since 20.0 */ public static final MediaType VND_WAVE_AUDIO = createConstant(AUDIO_TYPE, "vnd.wave"); /* video types */ public static final MediaType MP4_VIDEO = createConstant(VIDEO_TYPE, "mp4"); public static final MediaType MPEG_VIDEO = createConstant(VIDEO_TYPE, "mpeg"); public static final MediaType OGG_VIDEO = createConstant(VIDEO_TYPE, "ogg"); public static final MediaType QUICKTIME = createConstant(VIDEO_TYPE, "quicktime"); public static final MediaType WEBM_VIDEO = createConstant(VIDEO_TYPE, "webm"); public static final MediaType WMV = createConstant(VIDEO_TYPE, "x-ms-wmv"); /** * Flash video. For more information, see this link. * * @since 20.0 */ public static final MediaType FLV_VIDEO = createConstant(VIDEO_TYPE, "x-flv"); /** * The 3GP multimedia container format. For more information, see 3GPP TS * 26.244. * * @since 20.0 */ public static final MediaType THREE_GPP_VIDEO = createConstant(VIDEO_TYPE, "3gpp"); /** * The 3G2 multimedia container format. For more information, see 3GPP2 * C.S0050-B. * * @since 20.0 */ public static final MediaType THREE_GPP2_VIDEO = createConstant(VIDEO_TYPE, "3gpp2"); /* application types */ /** * As described in RFC 3023, this constant * ({@code application/xml}) is used for XML documents that are "unreadable by casual users." * {@link #XML_UTF_8} is provided for documents that may be read by users. * * @since 14.0 */ public static final MediaType APPLICATION_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xml"); public static final MediaType ATOM_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "atom+xml"); public static final MediaType BZIP2 = createConstant(APPLICATION_TYPE, "x-bzip2"); /** * Files in the dart * programming language. * * @since 19.0 */ public static final MediaType DART_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "dart"); /** * Apple Passbook. * * @since 19.0 */ public static final MediaType APPLE_PASSBOOK = createConstant(APPLICATION_TYPE, "vnd.apple.pkpass"); /** * Embedded OpenType fonts. This is * registered * with the IANA. * * @since 17.0 */ public static final MediaType EOT = createConstant(APPLICATION_TYPE, "vnd.ms-fontobject"); /** * As described in the International Digital Publishing Forum * EPUB is the distribution and interchange format standard for digital publications and * documents. This media type is defined in the EPUB Open Container Format * specification. * * @since 15.0 */ public static final MediaType EPUB = createConstant(APPLICATION_TYPE, "epub+zip"); public static final MediaType FORM_DATA = createConstant(APPLICATION_TYPE, "x-www-form-urlencoded"); /** * As described in PKCS #12: Personal * Information Exchange Syntax Standard, PKCS #12 defines an archive file format for storing * many cryptography objects as a single file. * * @since 15.0 */ public static final MediaType KEY_ARCHIVE = createConstant(APPLICATION_TYPE, "pkcs12"); /** * This is a non-standard media type, but is commonly used in serving hosted binary files as it is * * known not to trigger content sniffing in current browsers. It should not be used in * other situations as it is not specified by any RFC and does not appear in the /IANA MIME Media Types list. Consider * {@link #OCTET_STREAM} for binary data that is not being served to a browser. * * @since 14.0 */ public static final MediaType APPLICATION_BINARY = createConstant(APPLICATION_TYPE, "binary"); public static final MediaType GZIP = createConstant(APPLICATION_TYPE, "x-gzip"); /** * JSON Hypertext * Application Language (HAL) documents. * * @since 26.0 */ public static final MediaType HAL_JSON = createConstant(APPLICATION_TYPE, "hal+json"); /** * RFC 4329 declares this to be the * correct media type for JavaScript, but {@link #TEXT_JAVASCRIPT_UTF_8 text/javascript} may be * necessary in certain situations for compatibility. */ public static final MediaType JAVASCRIPT_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "javascript"); /** * For JWS or JWE objects using the Compact * Serialization. * * @since 27.1 */ public static final MediaType JOSE = createConstant(APPLICATION_TYPE, "jose"); /** * For JWS or JWE objects using the JSON * Serialization. * * @since 27.1 */ public static final MediaType JOSE_JSON = createConstant(APPLICATION_TYPE, "jose+json"); public static final MediaType JSON_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "json"); /** * The Manifest for a web application. * * @since 19.0 */ public static final MediaType MANIFEST_JSON_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "manifest+json"); /** * OGC KML (Keyhole Markup Language). */ public static final MediaType KML = createConstant(APPLICATION_TYPE, "vnd.google-earth.kml+xml"); /** * OGC KML (Keyhole Markup Language), * compressed using the ZIP format into KMZ archives. */ public static final MediaType KMZ = createConstant(APPLICATION_TYPE, "vnd.google-earth.kmz"); /** * The mbox database format. * * @since 13.0 */ public static final MediaType MBOX = createConstant(APPLICATION_TYPE, "mbox"); /** * Apple over-the-air mobile configuration profiles. * * @since 18.0 */ public static final MediaType APPLE_MOBILE_CONFIG = createConstant(APPLICATION_TYPE, "x-apple-aspen-config"); /** Microsoft Excel spreadsheets. */ public static final MediaType MICROSOFT_EXCEL = createConstant(APPLICATION_TYPE, "vnd.ms-excel"); /** * Microsoft Outlook items. * * @since 27.1 */ public static final MediaType MICROSOFT_OUTLOOK = createConstant(APPLICATION_TYPE, "vnd.ms-outlook"); /** Microsoft Powerpoint presentations. */ public static final MediaType MICROSOFT_POWERPOINT = createConstant(APPLICATION_TYPE, "vnd.ms-powerpoint"); /** Microsoft Word documents. */ public static final MediaType MICROSOFT_WORD = createConstant(APPLICATION_TYPE, "msword"); /** * WASM applications. For more information see the Web Assembly * overview. * * @since 27.0 */ public static final MediaType WASM_APPLICATION = createConstant(APPLICATION_TYPE, "wasm"); /** * NaCl applications. For more information see the * Developer Guide for Native Client Application Structure. * * @since 20.0 */ public static final MediaType NACL_APPLICATION = createConstant(APPLICATION_TYPE, "x-nacl"); /** * NaCl portable applications. For more information see the * Developer Guide for Native Client Application Structure. * * @since 20.0 */ public static final MediaType NACL_PORTABLE_APPLICATION = createConstant(APPLICATION_TYPE, "x-pnacl"); public static final MediaType OCTET_STREAM = createConstant(APPLICATION_TYPE, "octet-stream"); public static final MediaType OGG_CONTAINER = createConstant(APPLICATION_TYPE, "ogg"); public static final MediaType OOXML_DOCUMENT = createConstant( APPLICATION_TYPE, "vnd.openxmlformats-officedocument.wordprocessingml.document"); public static final MediaType OOXML_PRESENTATION = createConstant( APPLICATION_TYPE, "vnd.openxmlformats-officedocument.presentationml.presentation"); public static final MediaType OOXML_SHEET = createConstant(APPLICATION_TYPE, "vnd.openxmlformats-officedocument.spreadsheetml.sheet"); public static final MediaType OPENDOCUMENT_GRAPHICS = createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.graphics"); public static final MediaType OPENDOCUMENT_PRESENTATION = createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.presentation"); public static final MediaType OPENDOCUMENT_SPREADSHEET = createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.spreadsheet"); public static final MediaType OPENDOCUMENT_TEXT = createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.text"); public static final MediaType PDF = createConstant(APPLICATION_TYPE, "pdf"); public static final MediaType POSTSCRIPT = createConstant(APPLICATION_TYPE, "postscript"); /** * Protocol buffers * * @since 15.0 */ public static final MediaType PROTOBUF = createConstant(APPLICATION_TYPE, "protobuf"); /** * RDF/XML documents, which are XML * serializations of Resource Description * Framework graphs. * * @since 14.0 */ public static final MediaType RDF_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rdf+xml"); public static final MediaType RTF_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rtf"); /** * SFNT fonts (which includes TrueType and OpenType fonts). This is registered with * the IANA. * * @since 17.0 */ public static final MediaType SFNT = createConstant(APPLICATION_TYPE, "font-sfnt"); public static final MediaType SHOCKWAVE_FLASH = createConstant(APPLICATION_TYPE, "x-shockwave-flash"); /** * {@code skp} files produced by the 3D Modeling software SketchUp * * @since 13.0 */ public static final MediaType SKETCHUP = createConstant(APPLICATION_TYPE, "vnd.sketchup.skp"); /** * As described in RFC 3902, this constant * ({@code application/soap+xml}) is used to identify SOAP 1.2 message envelopes that have been * serialized with XML 1.0. * *

For SOAP 1.1 messages, see {@code XML_UTF_8} per W3C Note on Simple Object Access Protocol * (SOAP) 1.1 * * @since 20.0 */ public static final MediaType SOAP_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "soap+xml"); public static final MediaType TAR = createConstant(APPLICATION_TYPE, "x-tar"); /** * Web Open Font Format (WOFF) defined by the W3C. This is registered with * the IANA. * * @since 17.0 */ public static final MediaType WOFF = createConstant(APPLICATION_TYPE, "font-woff"); /** * Web Open Font Format (WOFF) * version 2 defined by the W3C. * * @since 20.0 */ public static final MediaType WOFF2 = createConstant(APPLICATION_TYPE, "font-woff2"); public static final MediaType XHTML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xhtml+xml"); /** * Extensible Resource Descriptors. This is not yet registered with the IANA, but it is specified * by OASIS in the XRD * definition and implemented in projects such as WebFinger. * * @since 14.0 */ public static final MediaType XRD_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xrd+xml"); public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip"); private final String type; private final String subtype; private final ImmutableListMultimap parameters; @LazyInit private String toString; @LazyInit private int hashCode; @LazyInit private Optional parsedCharset; private MediaType(String type, String subtype, ImmutableListMultimap parameters) { this.type = type; this.subtype = subtype; this.parameters = parameters; } /** Returns the top-level media type. For example, {@code "text"} in {@code "text/plain"}. */ public String type() { return type; } /** Returns the media subtype. For example, {@code "plain"} in {@code "text/plain"}. */ public String subtype() { return subtype; } /** Returns a multimap containing the parameters of this media type. */ public ImmutableListMultimap parameters() { return parameters; } private Map> parametersAsMap() { return Maps.transformValues( parameters.asMap(), new Function, ImmutableMultiset>() { @Override public ImmutableMultiset apply(Collection input) { return ImmutableMultiset.copyOf(input); } }); } /** * Returns an optional charset for the value of the charset parameter if it is specified. * * @throws IllegalStateException if multiple charset values have been set for this media type * @throws IllegalCharsetNameException if a charset value is present, but illegal * @throws UnsupportedCharsetException if a charset value is present, but no support is available * in this instance of the Java virtual machine */ public Optional charset() { // racy single-check idiom, this is safe because Optional is immutable. Optional local = parsedCharset; if (local == null) { String value = null; local = Optional.absent(); for (String currentValue : parameters.get(CHARSET_ATTRIBUTE)) { if (value == null) { value = currentValue; local = Optional.of(Charset.forName(value)); } else if (!value.equals(currentValue)) { throw new IllegalStateException( "Multiple charset values defined: " + value + ", " + currentValue); } } parsedCharset = local; } return local; } /** * Returns a new instance with the same type and subtype as this instance, but without any * parameters. */ public MediaType withoutParameters() { return parameters.isEmpty() ? this : create(type, subtype); } /** * Replaces all parameters with the given parameters. * * @throws IllegalArgumentException if any parameter or value is invalid */ public MediaType withParameters(Multimap parameters) { return create(type, subtype, parameters); } /** * Replaces all parameters with the given attribute with parameters using the given * values. If there are no values, any existing parameters with the given attribute are removed. * * @throws IllegalArgumentException if either {@code attribute} or {@code values} is invalid * @since 24.0 */ public MediaType withParameters(String attribute, Iterable values) { checkNotNull(attribute); checkNotNull(values); String normalizedAttribute = normalizeToken(attribute); ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); for (Entry entry : parameters.entries()) { String key = entry.getKey(); if (!normalizedAttribute.equals(key)) { builder.put(key, entry.getValue()); } } for (String value : values) { builder.put(normalizedAttribute, normalizeParameterValue(normalizedAttribute, value)); } MediaType mediaType = new MediaType(type, subtype, builder.build()); // if the attribute isn't charset, we can just inherit the current parsedCharset if (!normalizedAttribute.equals(CHARSET_ATTRIBUTE)) { mediaType.parsedCharset = this.parsedCharset; } // Return one of the constants if the media type is a known type. return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); } /** * Replaces all parameters with the given attribute with a single parameter with the * given value. If multiple parameters with the same attributes are necessary use {@link * #withParameters(String, Iterable)}. Prefer {@link #withCharset} for setting the {@code charset} * parameter when using a {@link Charset} object. * * @throws IllegalArgumentException if either {@code attribute} or {@code value} is invalid */ public MediaType withParameter(String attribute, String value) { return withParameters(attribute, ImmutableSet.of(value)); } /** * Returns a new instance with the same type and subtype as this instance, with the {@code * charset} parameter set to the {@link Charset#name name} of the given charset. Only one {@code * charset} parameter will be present on the new instance regardless of the number set on this * one. * *

If a charset must be specified that is not supported on this JVM (and thus is not * representable as a {@link Charset} instance, use {@link #withParameter}. */ public MediaType withCharset(Charset charset) { checkNotNull(charset); MediaType withCharset = withParameter(CHARSET_ATTRIBUTE, charset.name()); // precache the charset so we don't need to parse it withCharset.parsedCharset = Optional.of(charset); return withCharset; } /** Returns true if either the type or subtype is the wildcard. */ public boolean hasWildcard() { return WILDCARD.equals(type) || WILDCARD.equals(subtype); } /** * Returns {@code true} if this instance falls within the range (as defined by the HTTP Accept header) given * by the argument according to three criteria: * *

    *
  1. The type of the argument is the wildcard or equal to the type of this instance. *
  2. The subtype of the argument is the wildcard or equal to the subtype of this instance. *
  3. All of the parameters present in the argument are present in this instance. *
* *

For example: * *

{@code
   * PLAIN_TEXT_UTF_8.is(PLAIN_TEXT_UTF_8) // true
   * PLAIN_TEXT_UTF_8.is(HTML_UTF_8) // false
   * PLAIN_TEXT_UTF_8.is(ANY_TYPE) // true
   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE) // true
   * PLAIN_TEXT_UTF_8.is(ANY_IMAGE_TYPE) // false
   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_8)) // true
   * PLAIN_TEXT_UTF_8.withoutParameters().is(ANY_TEXT_TYPE.withCharset(UTF_8)) // false
   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_16)) // false
   * }
* *

Note that while it is possible to have the same parameter declared multiple times within a * media type this method does not consider the number of occurrences of a parameter. For example, * {@code "text/plain; charset=UTF-8"} satisfies {@code "text/plain; charset=UTF-8; * charset=UTF-8"}. */ public boolean is(MediaType mediaTypeRange) { return (mediaTypeRange.type.equals(WILDCARD) || mediaTypeRange.type.equals(this.type)) && (mediaTypeRange.subtype.equals(WILDCARD) || mediaTypeRange.subtype.equals(this.subtype)) && this.parameters.entries().containsAll(mediaTypeRange.parameters.entries()); } /** * Creates a new media type with the given type and subtype. * * @throws IllegalArgumentException if type or subtype is invalid or if a wildcard is used for the * type, but not the subtype. */ public static MediaType create(String type, String subtype) { MediaType mediaType = create(type, subtype, ImmutableListMultimap.of()); mediaType.parsedCharset = Optional.absent(); return mediaType; } private static MediaType create( String type, String subtype, Multimap parameters) { checkNotNull(type); checkNotNull(subtype); checkNotNull(parameters); String normalizedType = normalizeToken(type); String normalizedSubtype = normalizeToken(subtype); checkArgument( !WILDCARD.equals(normalizedType) || WILDCARD.equals(normalizedSubtype), "A wildcard type cannot be used with a non-wildcard subtype"); ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); for (Entry entry : parameters.entries()) { String attribute = normalizeToken(entry.getKey()); builder.put(attribute, normalizeParameterValue(attribute, entry.getValue())); } MediaType mediaType = new MediaType(normalizedType, normalizedSubtype, builder.build()); // Return one of the constants if the media type is a known type. return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); } /** * Creates a media type with the "application" type and the given subtype. * * @throws IllegalArgumentException if subtype is invalid */ static MediaType createApplicationType(String subtype) { return create(APPLICATION_TYPE, subtype); } /** * Creates a media type with the "audio" type and the given subtype. * * @throws IllegalArgumentException if subtype is invalid */ static MediaType createAudioType(String subtype) { return create(AUDIO_TYPE, subtype); } /** * Creates a media type with the "image" type and the given subtype. * * @throws IllegalArgumentException if subtype is invalid */ static MediaType createImageType(String subtype) { return create(IMAGE_TYPE, subtype); } /** * Creates a media type with the "text" type and the given subtype. * * @throws IllegalArgumentException if subtype is invalid */ static MediaType createTextType(String subtype) { return create(TEXT_TYPE, subtype); } /** * Creates a media type with the "video" type and the given subtype. * * @throws IllegalArgumentException if subtype is invalid */ static MediaType createVideoType(String subtype) { return create(VIDEO_TYPE, subtype); } private static String normalizeToken(String token) { checkArgument(TOKEN_MATCHER.matchesAllOf(token)); return Ascii.toLowerCase(token); } private static String normalizeParameterValue(String attribute, String value) { return CHARSET_ATTRIBUTE.equals(attribute) ? Ascii.toLowerCase(value) : value; } /** * Parses a media type from its string representation. * * @throws IllegalArgumentException if the input is not parsable */ public static MediaType parse(String input) { checkNotNull(input); Tokenizer tokenizer = new Tokenizer(input); try { String type = tokenizer.consumeToken(TOKEN_MATCHER); tokenizer.consumeCharacter('/'); String subtype = tokenizer.consumeToken(TOKEN_MATCHER); ImmutableListMultimap.Builder parameters = ImmutableListMultimap.builder(); while (tokenizer.hasMore()) { tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); tokenizer.consumeCharacter(';'); tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); String attribute = tokenizer.consumeToken(TOKEN_MATCHER); tokenizer.consumeCharacter('='); final String value; if ('"' == tokenizer.previewChar()) { tokenizer.consumeCharacter('"'); StringBuilder valueBuilder = new StringBuilder(); while ('"' != tokenizer.previewChar()) { if ('\\' == tokenizer.previewChar()) { tokenizer.consumeCharacter('\\'); valueBuilder.append(tokenizer.consumeCharacter(ascii())); } else { valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER)); } } value = valueBuilder.toString(); tokenizer.consumeCharacter('"'); } else { value = tokenizer.consumeToken(TOKEN_MATCHER); } parameters.put(attribute, value); } return create(type, subtype, parameters.build()); } catch (IllegalStateException e) { throw new IllegalArgumentException("Could not parse '" + input + "'", e); } } private static final class Tokenizer { final String input; int position = 0; Tokenizer(String input) { this.input = input; } String consumeTokenIfPresent(CharMatcher matcher) { checkState(hasMore()); int startPosition = position; position = matcher.negate().indexIn(input, startPosition); return hasMore() ? input.substring(startPosition, position) : input.substring(startPosition); } String consumeToken(CharMatcher matcher) { int startPosition = position; String token = consumeTokenIfPresent(matcher); checkState(position != startPosition); return token; } char consumeCharacter(CharMatcher matcher) { checkState(hasMore()); char c = previewChar(); checkState(matcher.matches(c)); position++; return c; } char consumeCharacter(char c) { checkState(hasMore()); checkState(previewChar() == c); position++; return c; } char previewChar() { checkState(hasMore()); return input.charAt(position); } boolean hasMore() { return (position >= 0) && (position < input.length()); } } @Override public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } else if (obj instanceof MediaType) { MediaType that = (MediaType) obj; return this.type.equals(that.type) && this.subtype.equals(that.subtype) // compare parameters regardless of order && this.parametersAsMap().equals(that.parametersAsMap()); } else { return false; } } @Override public int hashCode() { // racy single-check idiom int h = hashCode; if (h == 0) { h = Objects.hashCode(type, subtype, parametersAsMap()); hashCode = h; } return h; } private static final MapJoiner PARAMETER_JOINER = Joiner.on("; ").withKeyValueSeparator("="); /** * Returns the string representation of this media type in the format described in RFC 2045. */ @Override public String toString() { // racy single-check idiom, safe because String is immutable String result = toString; if (result == null) { result = computeToString(); toString = result; } return result; } private String computeToString() { StringBuilder builder = new StringBuilder().append(type).append('/').append(subtype); if (!parameters.isEmpty()) { builder.append("; "); Multimap quotedParameters = Multimaps.transformValues( parameters, new Function() { @Override public String apply(String value) { return TOKEN_MATCHER.matchesAllOf(value) ? value : escapeAndQuote(value); } }); PARAMETER_JOINER.appendTo(builder, quotedParameters.entries()); } return builder.toString(); } private static String escapeAndQuote(String value) { StringBuilder escaped = new StringBuilder(value.length() + 16).append('"'); for (int i = 0; i < value.length(); i++) { char ch = value.charAt(i); if (ch == '\r' || ch == '\\' || ch == '"') { escaped.append('\\'); } escaped.append(ch); } return escaped.append('"').toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy