From 829f299833a94f9122bb5b74379fafdc70429983 Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Mon, 25 Sep 2023 03:16:47 +0200 Subject: [PATCH] Navigator: Sort files and folders alphanumerically --- .../navigator/impl/NavigatorFolderNode.java | 3 +- .../model/util/AlphanumericComparator.java | 60 ++++++++++++++++ .../util/AlphanumericComparatorTest.java | 70 +++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 modules/platform-model/src/main/java/com/shade/platform/model/util/AlphanumericComparator.java create mode 100644 modules/platform-model/src/test/java/com/shade/platform/util/AlphanumericComparatorTest.java diff --git a/modules/decima-ui/src/main/java/com/shade/decima/ui/navigator/impl/NavigatorFolderNode.java b/modules/decima-ui/src/main/java/com/shade/decima/ui/navigator/impl/NavigatorFolderNode.java index 884986dcf..4d89f24d6 100644 --- a/modules/decima-ui/src/main/java/com/shade/decima/ui/navigator/impl/NavigatorFolderNode.java +++ b/modules/decima-ui/src/main/java/com/shade/decima/ui/navigator/impl/NavigatorFolderNode.java @@ -4,6 +4,7 @@ import com.shade.decima.ui.navigator.NavigatorPath; import com.shade.decima.ui.navigator.NavigatorSettings; import com.shade.platform.model.runtime.ProgressMonitor; +import com.shade.platform.model.util.AlphanumericComparator; import com.shade.util.NotNull; import com.shade.util.Nullable; @@ -13,7 +14,7 @@ public class NavigatorFolderNode extends NavigatorNode { private static final Comparator CHILDREN_COMPARATOR = Comparator .comparingInt((NavigatorNode node) -> node instanceof NavigatorFolderNode ? -1 : 1) - .thenComparing(NavigatorNode::getLabel); + .thenComparing(NavigatorNode::getLabel, AlphanumericComparator.getInstance()); private final FilePath path; diff --git a/modules/platform-model/src/main/java/com/shade/platform/model/util/AlphanumericComparator.java b/modules/platform-model/src/main/java/com/shade/platform/model/util/AlphanumericComparator.java new file mode 100644 index 000000000..6327f4ede --- /dev/null +++ b/modules/platform-model/src/main/java/com/shade/platform/model/util/AlphanumericComparator.java @@ -0,0 +1,60 @@ +package com.shade.platform.model.util; + +import com.shade.util.NotNull; + +import java.util.Comparator; + +public class AlphanumericComparator implements Comparator { + private static final AlphanumericComparator INSTANCE = new AlphanumericComparator(); + + private AlphanumericComparator() { + // prevents instantiation + } + + @NotNull + public static AlphanumericComparator getInstance() { + return INSTANCE; + } + + @Override + public int compare(CharSequence o1, CharSequence o2) { + final int len1 = o1.length(); + final int len2 = o2.length(); + + int i = 0; + int j = 0; + + while (i < len1 && j < len2) { + final char ch1 = o1.charAt(i); + final char ch2 = o2.charAt(i); + + if (Character.isDigit(ch1) && Character.isDigit(ch2)) { + int num1 = 0; + int num2 = 0; + + while (i < len1 && Character.isDigit(o1.charAt(i))) { + num1 = num1 * 10 + Character.digit(o1.charAt(i), 10); + i += 1; + } + + while (j < len2 && Character.isDigit(o2.charAt(j))) { + num2 = num2 * 10 + Character.digit(o2.charAt(j), 10); + j += 1; + } + + if (num1 != num2) { + return num1 - num2; + } + } else { + if (ch1 != ch2) { + return ch1 - ch2; + } + + i += 1; + j += 1; + } + } + + return len1 - len2; + } +} diff --git a/modules/platform-model/src/test/java/com/shade/platform/util/AlphanumericComparatorTest.java b/modules/platform-model/src/test/java/com/shade/platform/util/AlphanumericComparatorTest.java new file mode 100644 index 000000000..9351d8146 --- /dev/null +++ b/modules/platform-model/src/test/java/com/shade/platform/util/AlphanumericComparatorTest.java @@ -0,0 +1,70 @@ +package com.shade.platform.util; + +import com.shade.platform.model.util.AlphanumericComparator; +import com.shade.util.NotNull; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class AlphanumericComparatorTest { + @Test + public void alphanumericComparatorTest() { + alphanumericComparatorTestImpl( + List.of("img12", "img10", "img2", "img1", "img100_1", "img100_11", "img100", "img100_10", "img100_5"), + List.of("img1", "img2", "img10", "img12", "img100", "img100_1", "img100_5", "img100_10", "img100_11") + ); + + alphanumericComparatorTestImpl( + List.of("file1000", "file100", "file10", "file2", "file1", "file11", "file200", "file20", "file2a", "file2b"), + List.of("file1", "file2", "file2a", "file2b", "file10", "file11", "file20", "file100", "file200", "file1000") + ); + + alphanumericComparatorTestImpl( + List.of("doc10", "doc2", "doc20", "doc1", "doc3", "doc15", "doc4"), + List.of("doc1", "doc2", "doc3", "doc4", "doc10", "doc15", "doc20") + ); + + alphanumericComparatorTestImpl( + List.of("page2", "page12", "page3", "page22", "page4", "page13", "page23"), + List.of("page2", "page3", "page4", "page12", "page13", "page22", "page23") + ); + + alphanumericComparatorTestImpl( + List.of("item-5", "item-1", "item-10", "item-2", "item-11", "item-3", "item-20", "item-15"), + List.of("item-1", "item-2", "item-3", "item-5", "item-10", "item-11", "item-15", "item-20") + ); + + alphanumericComparatorTestImpl( + List.of("abc123def45", "abc12def345", "abc1234def5", "abc12345def4", "abc1234def45", "abc12345def456"), + List.of("abc12def345", "abc123def45", "abc1234def5", "abc1234def45", "abc12345def4", "abc12345def456") + ); + + alphanumericComparatorTestImpl( + List.of("file01_v2", "file02_v1", "file10_v10", "file02_v10", "file10_v2", "file1_v10"), + List.of("file01_v2", "file1_v10", "file02_v1", "file02_v10", "file10_v2", "file10_v10") + ); + + alphanumericComparatorTestImpl( + List.of("chapter-2.1.1", "chapter-10.1.1", "chapter-2.1.10", "chapter-10.1.2", "chapter-2.1.2"), + List.of("chapter-2.1.1", "chapter-2.1.2", "chapter-2.1.10", "chapter-10.1.1", "chapter-10.1.2") + ); + + alphanumericComparatorTestImpl( + List.of("item_1_2_3", "item_2_1_3", "item_1_10_3", "item_2_2_1", "item_1_2_10", "item_2_1_10", "item_1_10_1"), + List.of("item_1_2_3", "item_1_2_10", "item_1_10_1", "item_1_10_3", "item_2_1_3", "item_2_1_10", "item_2_2_1") + ); + } + + private void alphanumericComparatorTestImpl( + @NotNull Collection input, + @NotNull Collection expected + ) { + final ArrayList sorted = new ArrayList<>(input); + sorted.sort(AlphanumericComparator.getInstance()); + + Assertions.assertEquals(expected, sorted); + } +}