mirror of
https://github.com/bitwarden/android.git
synced 2025-03-16 19:28:44 +03:00
PM-10071 ensure that lowercase letters take priority over the upperca… (#3707)
This commit is contained in:
parent
e717183239
commit
06f6f19255
9 changed files with 122 additions and 49 deletions
|
@ -0,0 +1,67 @@
|
|||
package com.x8bit.bitwarden.data.platform.util
|
||||
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* String [Comparator] where the characters are compared giving precedence to
|
||||
* special characters.
|
||||
*/
|
||||
object SpecialCharWithPrecedenceComparator : Comparator<String> {
|
||||
override fun compare(str1: String, str2: String): Int {
|
||||
val minLength = minOf(str1.length, str2.length)
|
||||
for (i in 0 until minLength) {
|
||||
val char1 = str1[i]
|
||||
val char2 = str2[i]
|
||||
val compareResult = compareCharsSpecialCharsWithPrecedence(char1, char2)
|
||||
if (compareResult != 0) {
|
||||
return compareResult
|
||||
}
|
||||
}
|
||||
// If all compared chars are the same give precedence to the shorter String.
|
||||
return str1.length - str2.length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two characters, where a special character is considered with higher precedence over
|
||||
* letters and numbers. If both characters are a letter and they are equal ignoring the case,
|
||||
* give priority to the lowercase instance. If they are both a digit or a non-equal letter
|
||||
* use the default [String.compareTo] converting the chars to the [Locale] specific uppercase
|
||||
* String.
|
||||
*/
|
||||
private fun compareCharsSpecialCharsWithPrecedence(c1: Char, c2: Char): Int {
|
||||
return when {
|
||||
c1.isLetterOrDigit() && !c2.isLetterOrDigit() -> 1
|
||||
!c1.isLetterOrDigit() && c2.isLetterOrDigit() -> -1
|
||||
c1.isLetter() && c2.isLetter() && c1.equals(other = c2, ignoreCase = true) -> {
|
||||
compareLettersLowerCaseFirst(c1 = c1, c2 = c2)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val upperCaseStr1 = c1.toString().uppercase(Locale.getDefault())
|
||||
val upperCaseStr2 = c2.toString().uppercase(Locale.getDefault())
|
||||
upperCaseStr1.compareTo(upperCaseStr2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two equal letters ignoring case (i.e. 'A' == 'a'), give precedence to the
|
||||
* the character which is lowercase. If both [c1] and [c2] are equal and the
|
||||
* same case return 0 to indicate they are the same.
|
||||
*/
|
||||
private fun compareLettersLowerCaseFirst(c1: Char, c2: Char): Int {
|
||||
require(
|
||||
value = c1.isLetter() &&
|
||||
c2.isLetter() &&
|
||||
c1.equals(other = c2, ignoreCase = true),
|
||||
) {
|
||||
"Both character must be the same letter, case does not matter."
|
||||
}
|
||||
|
||||
return when {
|
||||
!c1.isLowerCase() && c2.isLowerCase() -> 1
|
||||
c1.isLowerCase() && !c2.isLowerCase() -> -1
|
||||
else -> 0
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package com.x8bit.bitwarden.data.platform.util
|
||||
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Compare two characters, where a special character is considered with higher precedence over
|
||||
* letters and numbers. If both characters are a letter or a digit use the default
|
||||
* [Char.compareTo].
|
||||
*/
|
||||
private fun compareCharsSpecialCharsWithPrecedence(c1: Char, c2: Char): Int {
|
||||
return when {
|
||||
c1.isLetterOrDigit() && !c2.isLetterOrDigit() -> 1
|
||||
!c1.isLetterOrDigit() && c2.isLetterOrDigit() -> -1
|
||||
else -> c1.compareTo(c2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String [Comparator] where the characters are compared giving precedence to
|
||||
* special characters.
|
||||
*/
|
||||
object CompareStringSpecialCharWithPrecedence : Comparator<String> {
|
||||
override fun compare(str1: String, str2: String): Int {
|
||||
val uppercaseStr1 = str1.uppercase(Locale.getDefault())
|
||||
val uppercaseStr2 = str2.uppercase(Locale.getDefault())
|
||||
val minLength = minOf(uppercaseStr1.length, uppercaseStr2.length)
|
||||
for (i in 0 until minLength) {
|
||||
val char1 = uppercaseStr1[i]
|
||||
val char2 = uppercaseStr2[i]
|
||||
val compareResult = compareCharsSpecialCharsWithPrecedence(char1, char2)
|
||||
if (compareResult != 0) {
|
||||
return compareResult
|
||||
}
|
||||
}
|
||||
// If all compared chars are the same give precedence to the shorter String.
|
||||
return uppercaseStr1.length - uppercaseStr2.length
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ import com.bitwarden.vault.PasswordHistory
|
|||
import com.bitwarden.vault.SecureNote
|
||||
import com.bitwarden.vault.SecureNoteType
|
||||
import com.bitwarden.vault.UriMatchType
|
||||
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
|
||||
import com.x8bit.bitwarden.data.platform.util.SpecialCharWithPrecedenceComparator
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherRepromptTypeJson
|
||||
|
@ -560,7 +560,7 @@ fun FieldTypeJson.toSdkFieldType(): FieldType =
|
|||
fun List<CipherView>.sortAlphabetically(): List<CipherView> {
|
||||
return this.sortedWith(
|
||||
comparator = { cipher1, cipher2 ->
|
||||
CompareStringSpecialCharWithPrecedence.compare(cipher1.name, cipher2.name)
|
||||
SpecialCharWithPrecedenceComparator.compare(cipher1.name, cipher2.name)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.vault.repository.util
|
|||
|
||||
import com.bitwarden.vault.Collection
|
||||
import com.bitwarden.vault.CollectionView
|
||||
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
|
||||
import com.x8bit.bitwarden.data.platform.util.SpecialCharWithPrecedenceComparator
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ fun List<SyncResponseJson.Collection>.toEncryptedSdkCollectionList(): List<Colle
|
|||
fun List<CollectionView>.sortAlphabetically(): List<CollectionView> {
|
||||
return this.sortedWith(
|
||||
comparator = { collection1, collection2 ->
|
||||
CompareStringSpecialCharWithPrecedence.compare(collection1.name, collection2.name)
|
||||
SpecialCharWithPrecedenceComparator.compare(collection1.name, collection2.name)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.vault.repository.util
|
|||
|
||||
import com.bitwarden.vault.Folder
|
||||
import com.bitwarden.vault.FolderView
|
||||
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
|
||||
import com.x8bit.bitwarden.data.platform.util.SpecialCharWithPrecedenceComparator
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
|
||||
|
@ -38,7 +38,7 @@ fun Folder.toEncryptedNetworkFolder(): FolderJsonRequest =
|
|||
fun List<FolderView>.sortAlphabetically(): List<FolderView> {
|
||||
return this.sortedWith(
|
||||
comparator = { folder1, folder2 ->
|
||||
CompareStringSpecialCharWithPrecedence.compare(folder1.name, folder2.name)
|
||||
SpecialCharWithPrecedenceComparator.compare(folder1.name, folder2.name)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import com.bitwarden.send.SendFile
|
|||
import com.bitwarden.send.SendText
|
||||
import com.bitwarden.send.SendType
|
||||
import com.bitwarden.send.SendView
|
||||
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
|
||||
import com.x8bit.bitwarden.data.platform.util.SpecialCharWithPrecedenceComparator
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
|
@ -133,7 +133,7 @@ private fun SendTypeJson.toSdkSendType(): SendType =
|
|||
fun List<SendView>.sortAlphabetically(): List<SendView> {
|
||||
return this.sortedWith(
|
||||
comparator = { send1, send2 ->
|
||||
CompareStringSpecialCharWithPrecedence.compare(send1.name, send2.name)
|
||||
SpecialCharWithPrecedenceComparator.compare(send1.name, send2.name)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import com.bitwarden.vault.CipherView
|
|||
import com.bitwarden.vault.CollectionView
|
||||
import com.bitwarden.vault.FolderView
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.platform.util.CompareStringSpecialCharWithPrecedence
|
||||
import com.x8bit.bitwarden.data.platform.util.SpecialCharWithPrecedenceComparator
|
||||
import com.x8bit.bitwarden.data.platform.util.subtitle
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.removeDiacritics
|
||||
|
@ -369,7 +369,7 @@ private fun SendView.toDisplayItem(
|
|||
*/
|
||||
private fun List<SearchState.DisplayItem>.sortAlphabetically(): List<SearchState.DisplayItem> {
|
||||
return this.sortedWith { item1, item2 ->
|
||||
CompareStringSpecialCharWithPrecedence.compare(item1.title, item2.title)
|
||||
SpecialCharWithPrecedenceComparator.compare(item1.title, item2.title)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package com.x8bit.bitwarden.data.platform.util
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
class SpecialCharWithPrecedenceComparatorTest {
|
||||
|
||||
@Test
|
||||
fun `Sorting with comparator should return expected result of sorted string`() {
|
||||
val unsortedList = listOf(
|
||||
"__Za",
|
||||
"z",
|
||||
"___",
|
||||
"1a3",
|
||||
"aBc",
|
||||
"__a",
|
||||
"__A",
|
||||
"__a",
|
||||
"__4",
|
||||
"Z",
|
||||
"__3",
|
||||
"Abc",
|
||||
)
|
||||
val expectedSortedList = listOf(
|
||||
"___",
|
||||
"__3",
|
||||
"__4",
|
||||
"__a",
|
||||
"__a",
|
||||
"__A",
|
||||
"__Za",
|
||||
"1a3",
|
||||
"aBc",
|
||||
"Abc",
|
||||
"z",
|
||||
"Z",
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expectedSortedList,
|
||||
unsortedList.sortedWith(SpecialCharWithPrecedenceComparator),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -318,8 +318,8 @@ class VaultSdkCipherExtensionsTest {
|
|||
createMockCipherView(1).copy(name = "_"),
|
||||
createMockCipherView(1).copy(name = "7"),
|
||||
createMockCipherView(1).copy(name = "8"),
|
||||
createMockCipherView(1).copy(name = "A"),
|
||||
createMockCipherView(1).copy(name = "aAb"),
|
||||
createMockCipherView(1).copy(name = "A"),
|
||||
createMockCipherView(1).copy(name = "AbA"),
|
||||
createMockCipherView(1).copy(name = "B"),
|
||||
createMockCipherView(1).copy(name = "c"),
|
||||
|
|
Loading…
Add table
Reference in a new issue