blob: 966f445889a5fb4bee34044c35f48bb2df1756fd [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you 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 org.apache.juneau;
import java.util.*;
import java.util.concurrent.*;
import org.apache.juneau.annotation.*;
/**
* Describes a single media type used in content negotiation between an HTTP client and server, as described in
* Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification).
*/
@BeanIgnore
public final class MediaType {
private static final ConcurrentHashMap<String,MediaType> cache = new ConcurrentHashMap<String,MediaType>();
/** Reusable predefined media type */
@SuppressWarnings("javadoc")
public static final MediaType
CSV = forString("text/csv"),
HTML = forString("text/html"),
JSON = forString("application/json"),
MSGPACK = forString("octal/msgpack"),
PLAIN = forString("text/plain"),
UON = forString("text/uon"),
URLENCODING = forString("application/x-www-form-urlencoded"),
XML = forString("text/xml"),
XMLSOAP = forString("text/xml+soap"),
RDF = forString("text/xml+rdf"),
RDFABBREV = forString("text/xml+rdf+abbrev"),
NTRIPLE = forString("text/n-triple"),
TURTLE = forString("text/turtle"),
N3 = forString("text/n3")
;
private final String mediaType;
private final String type; // The media type (e.g. "text" for Accept, "utf-8" for Accept-Charset)
private final String subType; // The media sub-type (e.g. "json" for Accept, not used for Accept-Charset)
/**
* Returns the media type for the specified string.
* The same media type strings always return the same objects so that these objects
* can be compared for equality using '=='.
* <p>
* <h5 class='section'>Notes:</h5>
* <ul>
* <li>Spaces are replaced with <js>'+'</js> characters.
* This gets around the issue where passing media type strings with <js>'+'</js> as HTTP GET parameters
* get replaced with spaces by your browser. Since spaces aren't supported by the spec, this
* is doesn't break anything.
* <li>Anything including and following the <js>';'</js> character is ignored (e.g. <js>";charset=X"</js>).
* </ul>
*
* @param s The media type string. Will be lowercased.
* <br>Returns <jk>null</jk> if input is null.
* @return A cached media type object.
*/
public static MediaType forString(String s) {
if (s == null)
return null;
MediaType mt = cache.get(s);
if (mt == null) {
mt = new MediaType(s);
cache.putIfAbsent(s, mt);
}
return cache.get(s);
}
private MediaType(String mt) {
int i = mt.indexOf(';');
if (i != -1)
mt = mt.substring(0, i);
mt = mt.toLowerCase(Locale.ENGLISH);
this.mediaType = mt;
String _type = null, _subType = null;
if (mt != null) {
mt = mt.replace(' ', '+');
i = mt.indexOf('/');
_type = (i == -1 ? mt : mt.substring(0, i));
_subType = (i == -1 ? "*" : mt.substring(i+1));
}
this.type = _type;
this.subType = _subType;
}
/**
* Returns the <js>'type'</js> fragment of the <js>'type/subType'</js> string.
*
* @return The media type.
*/
public String getType() {
return type;
}
/**
* Returns the <js>'subType'</js> fragment of the <js>'type/subType'</js> string.
*
* @return The media subtype.
*/
public String getSubType() {
return subType;
}
/**
* Returns <jk>true</jk> if this media type is a match for the specified media type.
* <p>
* Matches if any of the following is true:
* <ul>
* <li>Both type and subtype are the same.
* <li>One or both types are <js>'*'</js> and the subtypes are the same.
* <li>One or both subtypes are <js>'*'</js> and the types are the same.
* <li>Either is <js>'*\/*'</js>.
* </ul>
*
* @param o The media type to compare with.
* @return <jk>true</jk> if the media types match.
*/
public final boolean matches(MediaType o) {
if (this == o)
return true;
if (type.equals(o.type) || (type.equals("*")) || (o.type.equals("*")))
if (subType.equals(o.subType) || subType.equals("*") || o.subType.equals("*"))
return true;
return false;
}
@Override /* Object */
public String toString() {
return mediaType;
}
@Override /* Object */
public int hashCode() {
return mediaType.hashCode();
}
@Override /* Object */
public boolean equals(Object o) {
return this == o;
}
}