diff --git a/src/main/kotlin/com/bridgecrew/services/CheckovResultsComparatorGenerator.kt b/src/main/kotlin/com/bridgecrew/services/CheckovResultsComparatorGenerator.kt index e82d38c..48117a9 100644 --- a/src/main/kotlin/com/bridgecrew/services/CheckovResultsComparatorGenerator.kt +++ b/src/main/kotlin/com/bridgecrew/services/CheckovResultsComparatorGenerator.kt @@ -21,70 +21,62 @@ class CheckovResultsComparatorGenerator { .thenComparing(generateNameComparator(CheckovResultProperty.NAME)) } - fun generateResourceComparator(): Comparator { + private fun generateResourceComparator(): Comparator { return Comparator { result1, result2 -> if (result1.category != Category.SECRETS && result2.category != Category.SECRETS) { - return@Comparator compareResultsByName(CheckovResultProperty.RESOURCE, result1, result2) + return@Comparator generateNameComparator(CheckovResultProperty.RESOURCE).compare(result1, result2) } return@Comparator result1.category.compareTo(result2.category) } } - fun generateNameComparator(property: CheckovResultProperty): Comparator { + private fun generateNameComparator(property: CheckovResultProperty): Comparator { return Comparator { result1, result2 -> - return@Comparator compareResultsByName(property, result1, result2) + val name1 = extractNameByProperty(property, result1) + val name2 = extractNameByProperty(property, result2) + return@Comparator try { + getAlphanumericComparator().compare(name1, name2) + } catch (e: Exception) { + LOG.warn("Error while comparing ${property.name.lowercase()}", e) + name1.compareTo(name2, true) + } } } - fun compareResultsByName(property: CheckovResultProperty, result1: BaseCheckovResult, result2: BaseCheckovResult): Int { - - val name1 = extractNameByProperty(property, result1) - val name2 = extractNameByProperty(property, result2) - try { - if (name1.equals(name2, true)) - return 0 + // compare file names alphanumerically and also account for special characters like " ' / : . - _ + private fun getAlphanumericComparator(): Comparator { + return Comparator { a, b -> + val regex = Regex("(\\d+)|(\\D+)") + val partsA = regex.findAll(a).map { it.value } + val partsB = regex.findAll(b).map { it.value } - val regex = Regex("""\d+|\D+""") + val minSize = minOf(partsA.count(), partsB.count()) - val tokens1: List = regex.findAll(name1).map { it.groupValues.first() }.toList() - val tokens2: List = regex.findAll(name2).map { it.groupValues.first() }.toList() + for (i in 0 until minSize) { + val partA = partsA.elementAt(i) + val partB = partsB.elementAt(i) - if (tokens1.isEmpty() && tokens2.isEmpty()) - return name1.compareTo(name2, true) + val result = when { + partA.matches("\\d+".toRegex()) && partB.matches("\\d+".toRegex()) -> { + partA.toBigInteger().compareTo(partB.toBigInteger()) + } - if (tokens1.size == 1 || tokens2.size == 1) - return name1.compareTo(name2, true) + partA == partB -> 0 + else -> partA.compareTo(partB, ignoreCase = true) + } - var i = 0 - while (i < tokens1.size && i < tokens2.size && tokens1[i].equals(tokens2[i], true)) { - i++ + if (result != 0) { + return@Comparator result + } } - if (i >= tokens1.size) - return 1 - else if (i >= tokens2.size) - return -1 - - - if (tokens1[i].isEmpty() || tokens2[i].isEmpty()) - LOG.info("empty...") - val numberStr1 = tokens1[i].replace("\\D".toRegex(), "") - val numberStr2 = tokens2[i].replace("\\D".toRegex(), "") - - if (numberStr1.isEmpty() || numberStr2.isEmpty()) - return name1.compareTo(name2, true) - - return (numberStr1.toLong() - numberStr2.toLong()).toInt() - } catch (e: Exception) { - LOG.warn("Error while comparing ${property.name.lowercase()}", e) - return name1.compareTo(name2, true) + return@Comparator partsA.count().compareTo(partsB.count()) } - } - fun extractNameByProperty(property: CheckovResultProperty, checkovResult: BaseCheckovResult): String { + private fun extractNameByProperty(property: CheckovResultProperty, checkovResult: BaseCheckovResult): String { return when (property) { CheckovResultProperty.FILE_PATH -> { checkovResult.filePath