001 /*
002 * The contents of this file are subject to the terms
003 * of the Common Development and Distribution License
004 * (the "License"). You may not use this file except
005 * in compliance with the License.
006 *
007 * You can obtain a copy of the license at
008 * http://www.opensource.org/licenses/cddl1.php
009 * See the License for the specific language governing
010 * permissions and limitations under the License.
011 */
012
013 /*
014 * MediaType.java
015 *
016 * Created on March 22, 2007, 2:35 PM
017 *
018 */
019
020 package javax.ws.rs.core;
021
022 import java.util.Collections;
023 import java.util.Comparator;
024 import java.util.Map;
025 import java.util.TreeMap;
026 import javax.ws.rs.ext.RuntimeDelegate;
027 import javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate;
028
029 /**
030 * An abstraction for a media type. Instances are immutable.
031 * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP/1.1 section 3.7</a>
032 */
033 public class MediaType {
034
035 private String type;
036 private String subtype;
037 private Map<String, String> parameters;
038
039 /**
040 * Empty immutable map used for all instances without parameters
041 */
042 private static final Map<String, String> emptyMap = Collections.emptyMap();
043
044 private static final HeaderDelegate<MediaType> delegate =
045 RuntimeDelegate.getInstance().createHeaderDelegate(MediaType.class);
046
047 /** The value of a type or subtype wildcard: "*" */
048 public static final String MEDIA_TYPE_WILDCARD = "*";
049
050 // Common media type constants
051 /** "*/*" */
052 public final static String WILDCARD = "*/*";
053 /** "*/*" */
054 public final static MediaType WILDCARD_TYPE = new MediaType();
055
056 /** "application/xml" */
057 public final static String APPLICATION_XML = "application/xml";
058 /** "application/xml" */
059 public final static MediaType APPLICATION_XML_TYPE = new MediaType("application","xml");
060
061 /** "application/atom+xml" */
062 public final static String APPLICATION_ATOM_XML = "application/atom+xml";
063 /** "application/atom+xml" */
064 public final static MediaType APPLICATION_ATOM_XML_TYPE = new MediaType("application","atom+xml");
065
066 /** "application/xhtml+xml" */
067 public final static String APPLICATION_XHTML_XML = "application/xhtml+xml";
068 /** "application/xhtml+xml" */
069 public final static MediaType APPLICATION_XHTML_XML_TYPE = new MediaType("application","xhtml+xml");
070
071 /** "application/svg+xml" */
072 public final static String APPLICATION_SVG_XML = "application/svg+xml";
073 /** "application/svg+xml" */
074 public final static MediaType APPLICATION_SVG_XML_TYPE = new MediaType("application","svg+xml");
075
076 /** "application/json" */
077 public final static String APPLICATION_JSON = "application/json";
078 /** "application/json" */
079 public final static MediaType APPLICATION_JSON_TYPE = new MediaType("application","json");
080
081 /** "application/x-www-form-urlencoded" */
082 public final static String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
083 /** "application/x-www-form-urlencoded" */
084 public final static MediaType APPLICATION_FORM_URLENCODED_TYPE = new MediaType("application","x-www-form-urlencoded");
085
086 /** "multipart/form-data" */
087 public final static String MULTIPART_FORM_DATA = "multipart/form-data";
088 /** "multipart/form-data" */
089 public final static MediaType MULTIPART_FORM_DATA_TYPE = new MediaType("multipart","form-data");
090
091 /** "application/octet-stream" */
092 public final static String APPLICATION_OCTET_STREAM = "application/octet-stream";
093 /** "application/octet-stream" */
094 public final static MediaType APPLICATION_OCTET_STREAM_TYPE = new MediaType("application","octet-stream");
095
096 /** "text/plain" */
097 public final static String TEXT_PLAIN = "text/plain";
098 /** "text/plain" */
099 public final static MediaType TEXT_PLAIN_TYPE = new MediaType("text","plain");
100
101 /** "text/xml" */
102 public final static String TEXT_XML = "text/xml";
103 /** "text/xml" */
104 public final static MediaType TEXT_XML_TYPE = new MediaType("text","xml");
105
106 /** "text/html" */
107 public final static String TEXT_HTML = "text/html";
108 /** "text/html" */
109 public final static MediaType TEXT_HTML_TYPE = new MediaType("text","html");
110
111 /**
112 * Creates a new instance of MediaType by parsing the supplied string.
113 * @param type the media type string
114 * @return the newly created MediaType
115 * @throws IllegalArgumentException if the supplied string cannot be parsed
116 * or is null
117 */
118 public static MediaType valueOf(String type) throws IllegalArgumentException {
119 return delegate.fromString(type);
120 }
121
122 /**
123 * Creates a new instance of MediaType with the supplied type, subtype and
124 * parameters.
125 * @param type the primary type, null is equivalent to
126 * {@link #MEDIA_TYPE_WILDCARD}.
127 * @param subtype the subtype, null is equivalent to
128 * {@link #MEDIA_TYPE_WILDCARD}.
129 * @param parameters a map of media type parameters, null is the same as an
130 * empty map.
131 */
132 public MediaType(String type, String subtype, Map<String, String> parameters) {
133 this.type = type==null ? MEDIA_TYPE_WILDCARD : type;
134 this.subtype = subtype==null ? MEDIA_TYPE_WILDCARD : subtype;
135 if (parameters==null) {
136 this.parameters = emptyMap;
137 } else {
138 Map<String, String> map = new TreeMap<String, String>(new Comparator<String>() {
139 public int compare(String o1, String o2) {
140 return o1.compareToIgnoreCase(o2);
141 }
142 });
143 for (Map.Entry<String, String> e: parameters.entrySet()) {
144 map.put(e.getKey().toLowerCase(), e.getValue());
145 }
146 this.parameters = Collections.unmodifiableMap(map);
147 }
148 }
149
150 /**
151 * Creates a new instance of MediaType with the supplied type and subtype.
152 * @param type the primary type, null is equivalent to
153 * {@link #MEDIA_TYPE_WILDCARD}
154 * @param subtype the subtype, null is equivalent to
155 * {@link #MEDIA_TYPE_WILDCARD}
156 */
157 public MediaType(String type, String subtype) {
158 this(type,subtype,emptyMap);
159 }
160
161 /**
162 * Creates a new instance of MediaType, both type and subtype are wildcards.
163 * Consider using the constant {@link #WILDCARD_TYPE} instead.
164 */
165 public MediaType() {
166 this(MEDIA_TYPE_WILDCARD, MEDIA_TYPE_WILDCARD);
167 }
168
169 /**
170 * Getter for primary type.
171 * @return value of primary type.
172 */
173 public String getType() {
174 return this.type;
175 }
176
177 /**
178 * Checks if the primary type is a wildcard.
179 * @return true if the primary type is a wildcard
180 */
181 public boolean isWildcardType() {
182 return this.getType().equals(MEDIA_TYPE_WILDCARD);
183 }
184
185 /**
186 * Getter for subtype.
187 * @return value of subtype.
188 */
189 public String getSubtype() {
190 return this.subtype;
191 }
192
193 /**
194 * Checks if the subtype is a wildcard
195 * @return true if the subtype is a wildcard
196 */
197 public boolean isWildcardSubtype() {
198 return this.getSubtype().equals(MEDIA_TYPE_WILDCARD);
199 }
200
201 /**
202 * Getter for a read-only parameter map. Keys are case-insensitive.
203 * @return an immutable map of parameters.
204 */
205 public Map<String, String> getParameters() {
206 return parameters;
207 }
208
209 /**
210 * Check if this media type is compatible with another media type. E.g.
211 * image/* is compatible with image/jpeg, image/png, etc. Media type
212 * parameters are ignored. The function is commutative.
213 * @return true if the types are compatible, false otherwise.
214 * @param other the media type to compare with
215 */
216 public boolean isCompatible(MediaType other) {
217 if (other == null)
218 return false;
219 if (type.equals(MEDIA_TYPE_WILDCARD) || other.type.equals(MEDIA_TYPE_WILDCARD))
220 return true;
221 else if (type.equalsIgnoreCase(other.type) && (subtype.equals(MEDIA_TYPE_WILDCARD) || other.subtype.equals(MEDIA_TYPE_WILDCARD)))
222 return true;
223 else
224 return this.type.equalsIgnoreCase(other.type)
225 && this.subtype.equalsIgnoreCase(other.subtype);
226 }
227
228 /**
229 * Compares obj to this media type to see if they are the same by comparing
230 * type, subtype and parameters. Note that the case-sensitivity of parameter
231 * values is dependent on the semantics of the parameter name, see
232 * {@link <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP/1.1</a>}.
233 * This method assumes that values are case-sensitive.
234 * @param obj the object to compare to
235 * @return true if the two media types are the same, false otherwise.
236 */
237 @Override
238 public boolean equals(Object obj) {
239 if (obj == null)
240 return false;
241 if (!(obj instanceof MediaType))
242 return false;
243 MediaType other = (MediaType)obj;
244 return (this.type.equalsIgnoreCase(other.type)
245 && this.subtype.equalsIgnoreCase(other.subtype)
246 && this.parameters.equals(other.parameters));
247 }
248
249 /**
250 * Generate a hashcode from the type, subtype and parameters.
251 * @return a hashcode
252 */
253 @Override
254 public int hashCode() {
255 return (this.type.toLowerCase()+this.subtype.toLowerCase()).hashCode()+this.parameters.hashCode();
256 }
257
258 /**
259 * Convert the media type to a string suitable for use as the value of a
260 * corresponding HTTP header.
261 * @return a stringified media type
262 */
263 @Override
264 public String toString() {
265 return delegate.toString(this);
266 }
267 }