001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.http; 018 019import java.io.Serializable; 020import java.nio.charset.Charset; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.Comparator; 025import java.util.LinkedHashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.springframework.util.Assert; 030import org.springframework.util.CollectionUtils; 031import org.springframework.util.InvalidMimeTypeException; 032import org.springframework.util.MimeType; 033import org.springframework.util.MimeTypeUtils; 034import org.springframework.util.StringUtils; 035import org.springframework.util.comparator.CompoundComparator; 036 037/** 038 * A subclass of {@link MimeType} that adds support for quality parameters 039 * as defined in the HTTP specification. 040 * 041 * @author Arjen Poutsma 042 * @author Juergen Hoeller 043 * @author Rossen Stoyanchev 044 * @author Sebastien Deleuze 045 * @since 3.0 046 * @see <a href="https://tools.ietf.org/html/rfc7231#section-3.1.1.1"> 047 * HTTP 1.1: Semantics and Content, section 3.1.1.1</a> 048 */ 049public class MediaType extends MimeType implements Serializable { 050 051 private static final long serialVersionUID = 2069937152339670231L; 052 053 /** 054 * Public constant media type that includes all media ranges (i.e. "*/*"). 055 */ 056 public static final MediaType ALL; 057 058 /** 059 * A String equivalent of {@link MediaType#ALL}. 060 */ 061 public static final String ALL_VALUE = "*/*"; 062 063 /** 064 * Public constant media type for {@code application/atom+xml}. 065 */ 066 public final static MediaType APPLICATION_ATOM_XML; 067 068 /** 069 * A String equivalent of {@link MediaType#APPLICATION_ATOM_XML}. 070 */ 071 public final static String APPLICATION_ATOM_XML_VALUE = "application/atom+xml"; 072 073 /** 074 * Public constant media type for {@code application/x-www-form-urlencoded}. 075 */ 076 public final static MediaType APPLICATION_FORM_URLENCODED; 077 078 /** 079 * A String equivalent of {@link MediaType#APPLICATION_FORM_URLENCODED}. 080 */ 081 public final static String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded"; 082 083 /** 084 * Public constant media type for {@code application/json}. 085 */ 086 public final static MediaType APPLICATION_JSON; 087 088 /** 089 * A String equivalent of {@link MediaType#APPLICATION_JSON}. 090 * @see #APPLICATION_JSON_UTF8_VALUE 091 */ 092 public final static String APPLICATION_JSON_VALUE = "application/json"; 093 094 /** 095 * Public constant media type for {@code application/json;charset=UTF-8}. 096 */ 097 public final static MediaType APPLICATION_JSON_UTF8; 098 099 /** 100 * A String equivalent of {@link MediaType#APPLICATION_JSON_UTF8}. 101 */ 102 public final static String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8"; 103 104 /** 105 * Public constant media type for {@code application/octet-stream}. 106 */ 107 public final static MediaType APPLICATION_OCTET_STREAM; 108 109 /** 110 * A String equivalent of {@link MediaType#APPLICATION_OCTET_STREAM}. 111 */ 112 public final static String APPLICATION_OCTET_STREAM_VALUE = "application/octet-stream"; 113 114 /** 115 * Public constant media type for {@code application/pdf}. 116 * @since 4.3 117 */ 118 public final static MediaType APPLICATION_PDF; 119 120 /** 121 * A String equivalent of {@link MediaType#APPLICATION_PDF}. 122 * @since 4.3 123 */ 124 public final static String APPLICATION_PDF_VALUE = "application/pdf"; 125 126 /** 127 * Public constant media type for {@code application/rss+xml}. 128 * @since 4.3.6 129 */ 130 public final static MediaType APPLICATION_RSS_XML; 131 132 /** 133 * A String equivalent of {@link MediaType#APPLICATION_RSS_XML}. 134 * @since 4.3.6 135 */ 136 public final static String APPLICATION_RSS_XML_VALUE = "application/rss+xml"; 137 138 /** 139 * Public constant media type for {@code application/xhtml+xml}. 140 */ 141 public final static MediaType APPLICATION_XHTML_XML; 142 143 /** 144 * A String equivalent of {@link MediaType#APPLICATION_XHTML_XML}. 145 */ 146 public final static String APPLICATION_XHTML_XML_VALUE = "application/xhtml+xml"; 147 148 /** 149 * Public constant media type for {@code application/xml}. 150 */ 151 public final static MediaType APPLICATION_XML; 152 153 /** 154 * A String equivalent of {@link MediaType#APPLICATION_XML}. 155 */ 156 public final static String APPLICATION_XML_VALUE = "application/xml"; 157 158 /** 159 * Public constant media type for {@code image/gif}. 160 */ 161 public final static MediaType IMAGE_GIF; 162 163 /** 164 * A String equivalent of {@link MediaType#IMAGE_GIF}. 165 */ 166 public final static String IMAGE_GIF_VALUE = "image/gif"; 167 168 /** 169 * Public constant media type for {@code image/jpeg}. 170 */ 171 public final static MediaType IMAGE_JPEG; 172 173 /** 174 * A String equivalent of {@link MediaType#IMAGE_JPEG}. 175 */ 176 public final static String IMAGE_JPEG_VALUE = "image/jpeg"; 177 178 /** 179 * Public constant media type for {@code image/png}. 180 */ 181 public final static MediaType IMAGE_PNG; 182 183 /** 184 * A String equivalent of {@link MediaType#IMAGE_PNG}. 185 */ 186 public final static String IMAGE_PNG_VALUE = "image/png"; 187 188 /** 189 * Public constant media type for {@code multipart/form-data}. 190 */ 191 public final static MediaType MULTIPART_FORM_DATA; 192 193 /** 194 * A String equivalent of {@link MediaType#MULTIPART_FORM_DATA}. 195 */ 196 public final static String MULTIPART_FORM_DATA_VALUE = "multipart/form-data"; 197 198 /** 199 * Public constant media type for {@code text/event-stream}. 200 * @since 4.3.6 201 * @see <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events W3C recommendation</a> 202 */ 203 public final static MediaType TEXT_EVENT_STREAM; 204 205 /** 206 * A String equivalent of {@link MediaType#TEXT_EVENT_STREAM}. 207 * @since 4.3.6 208 */ 209 public final static String TEXT_EVENT_STREAM_VALUE = "text/event-stream"; 210 211 /** 212 * Public constant media type for {@code text/html}. 213 */ 214 public final static MediaType TEXT_HTML; 215 216 /** 217 * A String equivalent of {@link MediaType#TEXT_HTML}. 218 */ 219 public final static String TEXT_HTML_VALUE = "text/html"; 220 221 /** 222 * Public constant media type for {@code text/markdown}. 223 * @since 4.3 224 */ 225 public final static MediaType TEXT_MARKDOWN; 226 227 /** 228 * A String equivalent of {@link MediaType#TEXT_MARKDOWN}. 229 * @since 4.3 230 */ 231 public final static String TEXT_MARKDOWN_VALUE = "text/markdown"; 232 233 /** 234 * Public constant media type for {@code text/plain}. 235 */ 236 public final static MediaType TEXT_PLAIN; 237 238 /** 239 * A String equivalent of {@link MediaType#TEXT_PLAIN}. 240 */ 241 public final static String TEXT_PLAIN_VALUE = "text/plain"; 242 243 /** 244 * Public constant media type for {@code text/xml}. 245 */ 246 public final static MediaType TEXT_XML; 247 248 /** 249 * A String equivalent of {@link MediaType#TEXT_XML}. 250 */ 251 public final static String TEXT_XML_VALUE = "text/xml"; 252 253 private static final String PARAM_QUALITY_FACTOR = "q"; 254 255 256 static { 257 ALL = valueOf(ALL_VALUE); 258 APPLICATION_ATOM_XML = valueOf(APPLICATION_ATOM_XML_VALUE); 259 APPLICATION_FORM_URLENCODED = valueOf(APPLICATION_FORM_URLENCODED_VALUE); 260 APPLICATION_JSON = valueOf(APPLICATION_JSON_VALUE); 261 APPLICATION_JSON_UTF8 = valueOf(APPLICATION_JSON_UTF8_VALUE); 262 APPLICATION_OCTET_STREAM = valueOf(APPLICATION_OCTET_STREAM_VALUE); 263 APPLICATION_PDF = valueOf(APPLICATION_PDF_VALUE); 264 APPLICATION_RSS_XML = valueOf(APPLICATION_RSS_XML_VALUE); 265 APPLICATION_XHTML_XML = valueOf(APPLICATION_XHTML_XML_VALUE); 266 APPLICATION_XML = valueOf(APPLICATION_XML_VALUE); 267 IMAGE_GIF = valueOf(IMAGE_GIF_VALUE); 268 IMAGE_JPEG = valueOf(IMAGE_JPEG_VALUE); 269 IMAGE_PNG = valueOf(IMAGE_PNG_VALUE); 270 MULTIPART_FORM_DATA = valueOf(MULTIPART_FORM_DATA_VALUE); 271 TEXT_EVENT_STREAM = valueOf(TEXT_EVENT_STREAM_VALUE); 272 TEXT_HTML = valueOf(TEXT_HTML_VALUE); 273 TEXT_MARKDOWN = valueOf(TEXT_MARKDOWN_VALUE); 274 TEXT_PLAIN = valueOf(TEXT_PLAIN_VALUE); 275 TEXT_XML = valueOf(TEXT_XML_VALUE); 276 } 277 278 279 /** 280 * Create a new {@code MediaType} for the given primary type. 281 * <p>The {@linkplain #getSubtype() subtype} is set to "*", parameters empty. 282 * @param type the primary type 283 * @throws IllegalArgumentException if any of the parameters contain illegal characters 284 */ 285 public MediaType(String type) { 286 super(type); 287 } 288 289 /** 290 * Create a new {@code MediaType} for the given primary type and subtype. 291 * <p>The parameters are empty. 292 * @param type the primary type 293 * @param subtype the subtype 294 * @throws IllegalArgumentException if any of the parameters contain illegal characters 295 */ 296 public MediaType(String type, String subtype) { 297 super(type, subtype, Collections.<String, String>emptyMap()); 298 } 299 300 /** 301 * Create a new {@code MediaType} for the given type, subtype, and character set. 302 * @param type the primary type 303 * @param subtype the subtype 304 * @param charset the character set 305 * @throws IllegalArgumentException if any of the parameters contain illegal characters 306 */ 307 public MediaType(String type, String subtype, Charset charset) { 308 super(type, subtype, charset); 309 } 310 311 /** 312 * Create a new {@code MediaType} for the given type, subtype, and quality value. 313 * @param type the primary type 314 * @param subtype the subtype 315 * @param qualityValue the quality value 316 * @throws IllegalArgumentException if any of the parameters contain illegal characters 317 */ 318 public MediaType(String type, String subtype, double qualityValue) { 319 this(type, subtype, Collections.singletonMap(PARAM_QUALITY_FACTOR, Double.toString(qualityValue))); 320 } 321 322 /** 323 * Copy-constructor that copies the type, subtype and parameters of the given 324 * {@code MediaType}, and allows to set the specified character set. 325 * @param other the other media type 326 * @param charset the character set 327 * @throws IllegalArgumentException if any of the parameters contain illegal characters 328 * @since 4.3 329 */ 330 public MediaType(MediaType other, Charset charset) { 331 super(other, charset); 332 } 333 334 /** 335 * Copy-constructor that copies the type and subtype of the given {@code MediaType}, 336 * and allows for different parameters. 337 * @param other the other media type 338 * @param parameters the parameters, may be {@code null} 339 * @throws IllegalArgumentException if any of the parameters contain illegal characters 340 */ 341 public MediaType(MediaType other, Map<String, String> parameters) { 342 super(other.getType(), other.getSubtype(), parameters); 343 } 344 345 /** 346 * Create a new {@code MediaType} for the given type, subtype, and parameters. 347 * @param type the primary type 348 * @param subtype the subtype 349 * @param parameters the parameters, may be {@code null} 350 * @throws IllegalArgumentException if any of the parameters contain illegal characters 351 */ 352 public MediaType(String type, String subtype, Map<String, String> parameters) { 353 super(type, subtype, parameters); 354 } 355 356 357 @Override 358 protected void checkParameters(String attribute, String value) { 359 super.checkParameters(attribute, value); 360 if (PARAM_QUALITY_FACTOR.equals(attribute)) { 361 value = unquote(value); 362 double d = Double.parseDouble(value); 363 Assert.isTrue(d >= 0D && d <= 1D, 364 "Invalid quality value \"" + value + "\": should be between 0.0 and 1.0"); 365 } 366 } 367 368 /** 369 * Return the quality factor, as indicated by a {@code q} parameter, if any. 370 * Defaults to {@code 1.0}. 371 * @return the quality factor as double value 372 */ 373 public double getQualityValue() { 374 String qualityFactor = getParameter(PARAM_QUALITY_FACTOR); 375 return (qualityFactor != null ? Double.parseDouble(unquote(qualityFactor)) : 1D); 376 } 377 378 /** 379 * Indicate whether this {@code MediaType} includes the given media type. 380 * <p>For instance, {@code text/*} includes {@code text/plain} and {@code text/html}, 381 * and {@code application/*+xml} includes {@code application/soap+xml}, etc. 382 * This method is <b>not</b> symmetric. 383 * <p>Simply calls {@link MimeType#includes(MimeType)} but declared with a 384 * {@code MediaType} parameter for binary backwards compatibility. 385 * @param other the reference media type with which to compare 386 * @return {@code true} if this media type includes the given media type; 387 * {@code false} otherwise 388 */ 389 public boolean includes(MediaType other) { 390 return super.includes(other); 391 } 392 393 /** 394 * Indicate whether this {@code MediaType} is compatible with the given media type. 395 * <p>For instance, {@code text/*} is compatible with {@code text/plain}, 396 * {@code text/html}, and vice versa. In effect, this method is similar to 397 * {@link #includes}, except that it <b>is</b> symmetric. 398 * <p>Simply calls {@link MimeType#isCompatibleWith(MimeType)} but declared with a 399 * {@code MediaType} parameter for binary backwards compatibility. 400 * @param other the reference media type with which to compare 401 * @return {@code true} if this media type is compatible with the given media type; 402 * {@code false} otherwise 403 */ 404 public boolean isCompatibleWith(MediaType other) { 405 return super.isCompatibleWith(other); 406 } 407 408 /** 409 * Return a replica of this instance with the quality value of the given {@code MediaType}. 410 * @return the same instance if the given MediaType doesn't have a quality value, 411 * or a new one otherwise 412 */ 413 public MediaType copyQualityValue(MediaType mediaType) { 414 if (!mediaType.getParameters().containsKey(PARAM_QUALITY_FACTOR)) { 415 return this; 416 } 417 Map<String, String> params = new LinkedHashMap<String, String>(getParameters()); 418 params.put(PARAM_QUALITY_FACTOR, mediaType.getParameters().get(PARAM_QUALITY_FACTOR)); 419 return new MediaType(this, params); 420 } 421 422 /** 423 * Return a replica of this instance with its quality value removed. 424 * @return the same instance if the media type doesn't contain a quality value, 425 * or a new one otherwise 426 */ 427 public MediaType removeQualityValue() { 428 if (!getParameters().containsKey(PARAM_QUALITY_FACTOR)) { 429 return this; 430 } 431 Map<String, String> params = new LinkedHashMap<String, String>(getParameters()); 432 params.remove(PARAM_QUALITY_FACTOR); 433 return new MediaType(this, params); 434 } 435 436 437 /** 438 * Parse the given String value into a {@code MediaType} object, 439 * with this method name following the 'valueOf' naming convention 440 * (as supported by {@link org.springframework.core.convert.ConversionService}. 441 * @param value the string to parse 442 * @throws InvalidMediaTypeException if the media type value cannot be parsed 443 * @see #parseMediaType(String) 444 */ 445 public static MediaType valueOf(String value) { 446 return parseMediaType(value); 447 } 448 449 /** 450 * Parse the given String into a single {@code MediaType}. 451 * @param mediaType the string to parse 452 * @return the media type 453 * @throws InvalidMediaTypeException if the media type value cannot be parsed 454 */ 455 public static MediaType parseMediaType(String mediaType) { 456 MimeType type; 457 try { 458 type = MimeTypeUtils.parseMimeType(mediaType); 459 } 460 catch (InvalidMimeTypeException ex) { 461 throw new InvalidMediaTypeException(ex); 462 } 463 try { 464 return new MediaType(type.getType(), type.getSubtype(), type.getParameters()); 465 } 466 catch (IllegalArgumentException ex) { 467 throw new InvalidMediaTypeException(mediaType, ex.getMessage()); 468 } 469 } 470 471 /** 472 * Parse the comma-separated string into a list of {@code MediaType} objects. 473 * <p>This method can be used to parse an Accept or Content-Type header. 474 * @param mediaTypes the string to parse 475 * @return the list of media types 476 * @throws InvalidMediaTypeException if the media type value cannot be parsed 477 */ 478 public static List<MediaType> parseMediaTypes(String mediaTypes) { 479 if (!StringUtils.hasLength(mediaTypes)) { 480 return Collections.emptyList(); 481 } 482 String[] tokens = StringUtils.tokenizeToStringArray(mediaTypes, ","); 483 List<MediaType> result = new ArrayList<MediaType>(tokens.length); 484 for (String token : tokens) { 485 result.add(parseMediaType(token)); 486 } 487 return result; 488 } 489 490 /** 491 * Parse the given list of (potentially) comma-separated strings into a 492 * list of {@code MediaType} objects. 493 * <p>This method can be used to parse an Accept or Content-Type header. 494 * @param mediaTypes the string to parse 495 * @return the list of media types 496 * @throws InvalidMediaTypeException if the media type value cannot be parsed 497 * @since 4.3.2 498 */ 499 public static List<MediaType> parseMediaTypes(List<String> mediaTypes) { 500 if (CollectionUtils.isEmpty(mediaTypes)) { 501 return Collections.<MediaType>emptyList(); 502 } 503 else if (mediaTypes.size() == 1) { 504 return parseMediaTypes(mediaTypes.get(0)); 505 } 506 else { 507 List<MediaType> result = new ArrayList<MediaType>(8); 508 for (String mediaType : mediaTypes) { 509 result.addAll(parseMediaTypes(mediaType)); 510 } 511 return result; 512 } 513 } 514 515 /** 516 * Return a string representation of the given list of {@code MediaType} objects. 517 * <p>This method can be used to for an {@code Accept} or {@code Content-Type} header. 518 * @param mediaTypes the media types to create a string representation for 519 * @return the string representation 520 */ 521 public static String toString(Collection<MediaType> mediaTypes) { 522 return MimeTypeUtils.toString(mediaTypes); 523 } 524 525 /** 526 * Sorts the given list of {@code MediaType} objects by specificity. 527 * <p>Given two media types: 528 * <ol> 529 * <li>if either media type has a {@linkplain #isWildcardType() wildcard type}, then the media type without the 530 * wildcard is ordered before the other.</li> 531 * <li>if the two media types have different {@linkplain #getType() types}, then they are considered equal and 532 * remain their current order.</li> 533 * <li>if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype}, then the media type without 534 * the wildcard is sorted before the other.</li> 535 * <li>if the two media types have different {@linkplain #getSubtype() subtypes}, then they are considered equal 536 * and remain their current order.</li> 537 * <li>if the two media types have different {@linkplain #getQualityValue() quality value}, then the media type 538 * with the highest quality value is ordered before the other.</li> 539 * <li>if the two media types have a different amount of {@linkplain #getParameter(String) parameters}, then the 540 * media type with the most parameters is ordered before the other.</li> 541 * </ol> 542 * <p>For example: 543 * <blockquote>audio/basic < audio/* < */*</blockquote> 544 * <blockquote>audio/* < audio/*;q=0.7; audio/*;q=0.3</blockquote> 545 * <blockquote>audio/basic;level=1 < audio/basic</blockquote> 546 * <blockquote>audio/basic == text/html</blockquote> 547 * <blockquote>audio/basic == audio/wave</blockquote> 548 * @param mediaTypes the list of media types to be sorted 549 * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.3.2">HTTP 1.1: Semantics 550 * and Content, section 5.3.2</a> 551 */ 552 public static void sortBySpecificity(List<MediaType> mediaTypes) { 553 Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); 554 if (mediaTypes.size() > 1) { 555 Collections.sort(mediaTypes, SPECIFICITY_COMPARATOR); 556 } 557 } 558 559 /** 560 * Sorts the given list of {@code MediaType} objects by quality value. 561 * <p>Given two media types: 562 * <ol> 563 * <li>if the two media types have different {@linkplain #getQualityValue() quality value}, then the media type 564 * with the highest quality value is ordered before the other.</li> 565 * <li>if either media type has a {@linkplain #isWildcardType() wildcard type}, then the media type without the 566 * wildcard is ordered before the other.</li> 567 * <li>if the two media types have different {@linkplain #getType() types}, then they are considered equal and 568 * remain their current order.</li> 569 * <li>if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype}, then the media type without 570 * the wildcard is sorted before the other.</li> 571 * <li>if the two media types have different {@linkplain #getSubtype() subtypes}, then they are considered equal 572 * and remain their current order.</li> 573 * <li>if the two media types have a different amount of {@linkplain #getParameter(String) parameters}, then the 574 * media type with the most parameters is ordered before the other.</li> 575 * </ol> 576 * @param mediaTypes the list of media types to be sorted 577 * @see #getQualityValue() 578 */ 579 public static void sortByQualityValue(List<MediaType> mediaTypes) { 580 Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); 581 if (mediaTypes.size() > 1) { 582 Collections.sort(mediaTypes, QUALITY_VALUE_COMPARATOR); 583 } 584 } 585 586 /** 587 * Sorts the given list of {@code MediaType} objects by specificity as the 588 * primary criteria and quality value the secondary. 589 * @see MediaType#sortBySpecificity(List) 590 * @see MediaType#sortByQualityValue(List) 591 */ 592 public static void sortBySpecificityAndQuality(List<MediaType> mediaTypes) { 593 Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); 594 if (mediaTypes.size() > 1) { 595 Collections.sort(mediaTypes, new CompoundComparator<MediaType>( 596 MediaType.SPECIFICITY_COMPARATOR, MediaType.QUALITY_VALUE_COMPARATOR)); 597 } 598 } 599 600 601 /** 602 * Comparator used by {@link #sortByQualityValue(List)}. 603 */ 604 public static final Comparator<MediaType> QUALITY_VALUE_COMPARATOR = new Comparator<MediaType>() { 605 606 @Override 607 public int compare(MediaType mediaType1, MediaType mediaType2) { 608 double quality1 = mediaType1.getQualityValue(); 609 double quality2 = mediaType2.getQualityValue(); 610 int qualityComparison = Double.compare(quality2, quality1); 611 if (qualityComparison != 0) { 612 return qualityComparison; // audio/*;q=0.7 < audio/*;q=0.3 613 } 614 else if (mediaType1.isWildcardType() && !mediaType2.isWildcardType()) { // */* < audio/* 615 return 1; 616 } 617 else if (mediaType2.isWildcardType() && !mediaType1.isWildcardType()) { // audio/* > */* 618 return -1; 619 } 620 else if (!mediaType1.getType().equals(mediaType2.getType())) { // audio/basic == text/html 621 return 0; 622 } 623 else { // mediaType1.getType().equals(mediaType2.getType()) 624 if (mediaType1.isWildcardSubtype() && !mediaType2.isWildcardSubtype()) { // audio/* < audio/basic 625 return 1; 626 } 627 else if (mediaType2.isWildcardSubtype() && !mediaType1.isWildcardSubtype()) { // audio/basic > audio/* 628 return -1; 629 } 630 else if (!mediaType1.getSubtype().equals(mediaType2.getSubtype())) { // audio/basic == audio/wave 631 return 0; 632 } 633 else { 634 int paramsSize1 = mediaType1.getParameters().size(); 635 int paramsSize2 = mediaType2.getParameters().size(); 636 // audio/basic;level=1 < audio/basic 637 return (paramsSize2 < paramsSize1 ? -1 : (paramsSize2 == paramsSize1 ? 0 : 1)); 638 } 639 } 640 } 641 }; 642 643 644 /** 645 * Comparator used by {@link #sortBySpecificity(List)}. 646 */ 647 public static final Comparator<MediaType> SPECIFICITY_COMPARATOR = new SpecificityComparator<MediaType>() { 648 649 @Override 650 protected int compareParameters(MediaType mediaType1, MediaType mediaType2) { 651 double quality1 = mediaType1.getQualityValue(); 652 double quality2 = mediaType2.getQualityValue(); 653 int qualityComparison = Double.compare(quality2, quality1); 654 if (qualityComparison != 0) { 655 return qualityComparison; // audio/*;q=0.7 < audio/*;q=0.3 656 } 657 return super.compareParameters(mediaType1, mediaType2); 658 } 659 }; 660 661}