Refactor MimeType/MediaType specificity

This commit makes several changes to MimeType and MediaType
related to the topic of specificity.

This commit deprecates the MimeType and MediaType Comparators.
Comparators require a transitive relationship, and the desired order for
these types is not transitive (see #27488).

Instead, this commit introduces two new MimeType methods: isMoreSpecific
and isLessSpecific, both of which return booleans. MediaType overrides
these methods to include the quality factor (q) in the comparison.

All MediaType sorting methods have been deprecated in favor of
MimeTypeUtils::sortBySpecificity.  This sorting method now uses
MimeType::isLessSpecific in combination a bubble sort algorithm (which
does not require a transitive compare function).

Closes gh-27580
This commit is contained in:
Arjen Poutsma
2021-10-19 18:40:41 +02:00
committed by Arjen Poutsma
parent a7789db067
commit 6d9136013e
5 changed files with 342 additions and 40 deletions

View File

@@ -402,6 +402,69 @@ class MimeTypeTests {
assertThat(m2.compareTo(m1) != 0).as("Invalid comparison result").isTrue();
}
@Test
void isMoreSpecific() {
MimeType audioBasic = new MimeType("audio", "basic");
MimeType audio = new MimeType("audio");
MimeType audioWave = new MimeType("audio", "wave");
MimeType audioBasicLevel = new MimeType("audio", "basic", singletonMap("level", "1"));
assertThat(audioBasic.isMoreSpecific(audioBasicLevel)).isFalse();
assertThat(audioBasicLevel.isMoreSpecific(audioBasic)).isTrue();
assertThat(audio.isMoreSpecific(MimeTypeUtils.ALL)).isTrue();
assertThat(MimeTypeUtils.ALL.isMoreSpecific(audio)).isFalse();
assertThat(audioBasicLevel.isMoreSpecific(audioBasic)).isTrue();
assertThat(audioBasic.isMoreSpecific(audioBasicLevel)).isFalse();
assertThat(audioBasic.isMoreSpecific(MimeTypeUtils.TEXT_HTML)).isFalse();
assertThat(audioBasic.isMoreSpecific(audioWave)).isFalse();
assertThat(audioBasicLevel.isMoreSpecific(MimeTypeUtils.TEXT_HTML)).isFalse();
}
@Test
void isLessSpecific() {
MimeType audioBasic = new MimeType("audio", "basic");
MimeType audio = new MimeType("audio");
MimeType audioWave = new MimeType("audio", "wave");
MimeType audioBasicLevel = new MimeType("audio", "basic", singletonMap("level", "1"));
assertThat(audioBasic.isLessSpecific(audioBasicLevel)).isTrue();
assertThat(audioBasicLevel.isLessSpecific(audioBasic)).isFalse();
assertThat(audio.isLessSpecific(MimeTypeUtils.ALL)).isFalse();
assertThat(MimeTypeUtils.ALL.isLessSpecific(audio)).isTrue();
assertThat(audioBasicLevel.isLessSpecific(audioBasic)).isFalse();
assertThat(audioBasic.isLessSpecific(audioBasicLevel)).isTrue();
assertThat(audioBasic.isLessSpecific(MimeTypeUtils.TEXT_HTML)).isFalse();
assertThat(audioBasic.isLessSpecific(audioWave)).isFalse();
assertThat(audioBasicLevel.isLessSpecific(MimeTypeUtils.TEXT_HTML)).isFalse();
}
@Test
void sortBySpecificity() {
MimeType audioBasic = new MimeType("audio", "basic");
MimeType audio = new MimeType("audio");
MimeType audioWave = new MimeType("audio", "wave");
MimeType audioBasicLevel = new MimeType("audio", "basic", singletonMap("level", "1"));
List<MimeType> mimeTypes = new ArrayList<>(List.of(MimeTypeUtils.ALL, audio, audioWave, audioBasic,
audioBasicLevel));
MimeTypeUtils.sortBySpecificity(mimeTypes);
assertThat(mimeTypes).containsExactly(audioWave, audioBasicLevel, audioBasic, audio, MimeTypeUtils.ALL);
}
@Test
void bubbleSort() {
List<Integer> list = new ArrayList<>(List.of(10, 9, 8, 7, 6, 5, 4, 3, 2, 1));
MimeTypeUtils.bubbleSort(list, (i1, i2) -> i1 > i2);
assertThat(list).containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
@Test // SPR-13157
void equalsIsCaseInsensitiveForCharsets() {
MimeType m1 = new MimeType("text", "plain", singletonMap("charset", "UTF-8"));