From 099b6645e437eb54e06f0c6cf4fa508411faa785 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 27 Feb 2019 14:59:10 +0300 Subject: [PATCH] IDEA-207325 vcs: unify ChangesBrowser file ordering with Project View --- .../dvcs/ui/RepositoryChangesBrowserNode.kt | 3 +- .../ide/util/treeView/AlphaComparator.java | 29 +- .../util/text/NaturalFileNameComparator.java | 41 +++ .../HierarchicalFilePathComparator.java | 22 +- .../changes/ui/ChangeDiffRequestChain.java | 2 +- .../ui/ChangesBrowserChangeListNode.java | 2 +- .../changes/ui/ChangesBrowserFileNode.java | 10 +- .../ui/ChangesBrowserFilePathNode.java | 3 +- .../changes/ui/ChangesBrowserModuleNode.java | 2 +- .../vcs/changes/ui/ChangesBrowserNode.java | 14 +- .../vcs/changes/ui/ChangesComparator.java | 10 +- .../HierarchicalFilePathComparatorTest.java | 279 ++++++++++++++++-- 12 files changed, 339 insertions(+), 78 deletions(-) create mode 100644 platform/util/src/com/intellij/openapi/util/text/NaturalFileNameComparator.java diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/RepositoryChangesBrowserNode.kt b/platform/dvcs-impl/src/com/intellij/dvcs/ui/RepositoryChangesBrowserNode.kt index 9532d9c2f4e7..068d9116dc38 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/RepositoryChangesBrowserNode.kt +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/RepositoryChangesBrowserNode.kt @@ -59,7 +59,8 @@ class RepositoryChangesBrowserNode(repository: Repository) : ChangesBrowserNode< override fun getSortWeight(): Int = REPOSITORY_SORT_WEIGHT - override fun compareUserObjects(o2: Repository): Int = getShortRepositoryName(getUserObject()).compareTo(getShortRepositoryName(o2), true) + override fun compareUserObjects(o2: Repository): Int = + compareFileNames(getShortRepositoryName(getUserObject()), getShortRepositoryName(o2)) override fun getTextPresentation(): String = getShortRepositoryName(getUserObject()) diff --git a/platform/editor-ui-api/src/com/intellij/ide/util/treeView/AlphaComparator.java b/platform/editor-ui-api/src/com/intellij/ide/util/treeView/AlphaComparator.java index b87c3b69555d..1c28eaad584a 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/util/treeView/AlphaComparator.java +++ b/platform/editor-ui-api/src/com/intellij/ide/util/treeView/AlphaComparator.java @@ -15,8 +15,7 @@ */ package com.intellij.ide.util.treeView; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.text.NaturalFileNameComparator; import java.util.Comparator; @@ -38,30 +37,6 @@ public class AlphaComparator implements Comparator{ if (s1 == null) return s2 == null ? 0 : -1; if (s2 == null) return +1; - //for super natural comparison (IDEA-80435) - Pair normalized = normalize(s1, s2); - return StringUtil.naturalCompare(normalized.first, normalized.second); - } - - private static Pair normalize(String s1, String s2) { - final int minLen = Math.min(s1.length(), s2.length()); - final StringBuilder sb1 = new StringBuilder(s1); - final StringBuilder sb2 = new StringBuilder(s2); - for (int i = 0; i < minLen; i++) { - final char ch1 = s1.charAt(i); - final char ch2 = sb2.charAt(i); - if (ch1 == ch2 && ch1 == '-') { - sb1.setCharAt(i, '_'); - sb2.setCharAt(i, '_'); - } else if (ch1 == '-' && ch2 != '_') { - sb1.setCharAt(i, '_'); - } else if (ch2 == '-' && ch1 != '_') { - sb2.setCharAt(i, '_'); - } - } - - s1 = sb1.toString(); - s2 = sb2.toString(); - return Pair.create(s1, s2); + return NaturalFileNameComparator.INSTANCE.compare(s1, s2); } } diff --git a/platform/util/src/com/intellij/openapi/util/text/NaturalFileNameComparator.java b/platform/util/src/com/intellij/openapi/util/text/NaturalFileNameComparator.java new file mode 100644 index 000000000000..2fd70e1ed51c --- /dev/null +++ b/platform/util/src/com/intellij/openapi/util/text/NaturalFileNameComparator.java @@ -0,0 +1,41 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.openapi.util.text; + +import com.intellij.openapi.util.Pair; + +import java.util.Comparator; + +public class NaturalFileNameComparator implements Comparator { + public static final Comparator INSTANCE = new NaturalFileNameComparator(); + + @Override + public int compare(String s1, String s2) { + //for super natural comparison (IDEA-80435) + Pair normalized = normalize(s1, s2); + return StringUtil.naturalCompare(normalized.first, normalized.second); + } + + private static Pair normalize(String s1, String s2) { + final int minLen = Math.min(s1.length(), s2.length()); + final StringBuilder sb1 = new StringBuilder(s1); + final StringBuilder sb2 = new StringBuilder(s2); + for (int i = 0; i < minLen; i++) { + final char ch1 = s1.charAt(i); + final char ch2 = sb2.charAt(i); + if (ch1 == ch2 && ch1 == '-') { + sb1.setCharAt(i, '_'); + sb2.setCharAt(i, '_'); + } + else if (ch1 == '-' && ch2 != '_') { + sb1.setCharAt(i, '_'); + } + else if (ch2 == '-' && ch1 != '_') { + sb2.setCharAt(i, '_'); + } + } + + s1 = sb1.toString(); + s2 = sb2.toString(); + return Pair.create(s1, s2); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/HierarchicalFilePathComparator.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/HierarchicalFilePathComparator.java index 48c83127cc13..3e50695e1ca7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/HierarchicalFilePathComparator.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/HierarchicalFilePathComparator.java @@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.changes; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtilRt; +import com.intellij.openapi.util.text.NaturalFileNameComparator; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.FilePath; import org.jetbrains.annotations.NotNull; @@ -28,9 +29,17 @@ import java.util.Comparator; * (like in default sorting method of most file managers). */ public class HierarchicalFilePathComparator implements Comparator { + public static final HierarchicalFilePathComparator CASE_SENSITIVE = new HierarchicalFilePathComparator(false); + public static final HierarchicalFilePathComparator CASE_INSENSITIVE = new HierarchicalFilePathComparator(true); + public static final HierarchicalFilePathComparator SYSTEM_CASE_SENSITIVE = SystemInfo.isFileSystemCaseSensitive ? CASE_SENSITIVE + : CASE_INSENSITIVE; - public static final HierarchicalFilePathComparator IGNORE_CASE = new HierarchicalFilePathComparator(true); - public static final HierarchicalFilePathComparator SYSTEM_CASE_SENSITIVE = new HierarchicalFilePathComparator(!SystemInfo.isFileSystemCaseSensitive); + public static final HierarchicalFilePathComparator NATURAL = new HierarchicalFilePathComparator(true) { + @Override + protected int compareFileNames(@NotNull String name1, @NotNull String name2) { + return NaturalFileNameComparator.INSTANCE.compare(name1, name2); + } + }; private final boolean myIgnoreCase; @@ -73,7 +82,14 @@ public class HierarchicalFilePathComparator implements Comparator { return isDirectory1 ? -1 : 1; } - return StringUtil.compare(name1, name2, myIgnoreCase); + return compareFileNames(name1, name2); } } + + /** + * NB: Overriding methods should not return 0, if base method does not. + */ + protected int compareFileNames(@NotNull String name1, @NotNull String name2) { + return StringUtil.compare(name1, name2, myIgnoreCase); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangeDiffRequestChain.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangeDiffRequestChain.java index 7d377ee31ec3..7b8be7012256 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangeDiffRequestChain.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangeDiffRequestChain.java @@ -162,7 +162,7 @@ public class ChangeDiffRequestChain extends DiffRequestChainBase implements GoTo @Override public int compareTo(@NotNull GenericChangesBrowserNode o) { - return myFilePath.getPath().compareToIgnoreCase(o.myFilePath.getPath()); + return compareFilePaths(myFilePath, o.myFilePath); } } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserChangeListNode.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserChangeListNode.java index 60b445ffb370..73ac2536f9df 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserChangeListNode.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserChangeListNode.java @@ -136,6 +136,6 @@ public class ChangesBrowserChangeListNode extends ChangesBrowserNode @Override public int compareUserObjects(final ChangeList o2) { - return getUserObject().getName().compareToIgnoreCase(o2.getName()); + return compareFileNames(getUserObject().getName(), o2.getName()); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserFileNode.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserFileNode.java index b2848da20bf1..c03550c5f6c2 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserFileNode.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserFileNode.java @@ -3,7 +3,6 @@ package com.intellij.openapi.vcs.changes.ui; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vfs.VirtualFile; @@ -19,7 +18,7 @@ public class ChangesBrowserFileNode extends ChangesBrowserNode impl public ChangesBrowserFileNode(@Nullable Project project, @NotNull VirtualFile userObject) { super(userObject); - myName = StringUtil.toLowerCase(userObject.getName()); + myName = userObject.getName(); myProject = project; } @@ -73,12 +72,7 @@ public class ChangesBrowserFileNode extends ChangesBrowserNode impl @Override public int compareTo(ChangesBrowserFileNode o) { - return myName.compareTo(o.myName); - } - - @Override - public int compareUserObjects(final VirtualFile o2) { - return getUserObject().getName().compareToIgnoreCase(o2.getName()); + return compareFileNames(myName, o.myName); } @NotNull diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserFilePathNode.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserFilePathNode.java index c48accb36b7e..527d3803795a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserFilePathNode.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserFilePathNode.java @@ -7,6 +7,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangesUtil; +import com.intellij.openapi.vcs.changes.HierarchicalFilePathComparator; import com.intellij.ui.SimpleTextAttributes; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -99,6 +100,6 @@ public class ChangesBrowserFilePathNode extends ChangesBrowserNode { @Override public int compareUserObjects(final FilePath o2) { - return getUserObject().getPath().compareToIgnoreCase(o2.getPath()); + return compareFilePaths(getUserObject(), o2); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserModuleNode.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserModuleNode.java index d58c89fcedcc..efbd56924c6c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserModuleNode.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserModuleNode.java @@ -58,7 +58,7 @@ public class ChangesBrowserModuleNode extends ChangesBrowserNode { @Override public int compareUserObjects(final Module o2) { - return getUserObject().getName().compareToIgnoreCase(o2.getName()); + return compareFileNames(getUserObject().getName(), o2.getName()); } @NotNull diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java index 3495617a6b35..136459f94fa9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java @@ -6,12 +6,10 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.UserDataHolderEx; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.NaturalFileNameComparator; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsBundle; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ChangeListOwner; -import com.intellij.openapi.vcs.changes.LocallyDeletedChange; -import com.intellij.openapi.vcs.changes.LogicalLock; +import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.SimpleTextAttributes; @@ -321,6 +319,14 @@ public class ChangesBrowserNode extends DefaultMutableTreeNode implements Use return 0; } + protected static int compareFileNames(@NotNull String name1, @NotNull String name2) { + return NaturalFileNameComparator.INSTANCE.compare(name1, name2); + } + + protected static int compareFilePaths(@NotNull FilePath path1, @NotNull FilePath path2) { + return HierarchicalFilePathComparator.NATURAL.compare(path1, path2); + } + public void setAttributes(@NotNull SimpleTextAttributes attributes) { myAttributes = attributes; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesComparator.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesComparator.java index ae768c0712a6..844a49cfcd3c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesComparator.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesComparator.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.changes.ui; +import com.intellij.openapi.util.text.NaturalFileNameComparator; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeListChange; @@ -44,14 +45,11 @@ public class ChangesComparator { private static int comparePaths(@NotNull FilePath filePath1, @NotNull FilePath filePath2, boolean flattened) { - if (!flattened) { - return HierarchicalFilePathComparator.IGNORE_CASE.compare(filePath1, filePath2); - } - else { - int delta = filePath1.getName().compareToIgnoreCase(filePath2.getName()); + if (flattened) { + int delta = NaturalFileNameComparator.INSTANCE.compare(filePath1.getName(), filePath2.getName()); if (delta != 0) return delta; - return filePath1.getPath().compareTo(filePath2.getPath()); } + return HierarchicalFilePathComparator.NATURAL.compare(filePath1, filePath2); } private static class VirtualFileComparator implements Comparator { diff --git a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/ui/HierarchicalFilePathComparatorTest.java b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/ui/HierarchicalFilePathComparatorTest.java index d6df64073f76..f90a91cf4d84 100644 --- a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/ui/HierarchicalFilePathComparatorTest.java +++ b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/ui/HierarchicalFilePathComparatorTest.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.vcs.changes.ui; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.LocalFilePath; @@ -25,8 +24,10 @@ import com.intellij.util.containers.ContainerUtil; import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; -import java.util.Collection; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.TreeSet; public class HierarchicalFilePathComparatorTest extends TestCase { public void testInOneDirectory() { @@ -87,8 +88,174 @@ public class HierarchicalFilePathComparatorTest extends TestCase { assertEquals(-1, compare("/folder/abx/", "/folder/abd")); } + public void testCaseSensitiveOrder() { + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_SENSITIVE, + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_12", + "/a/Test1.txt_2", + "/a/Test1.txt_22" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_SENSITIVE, + "/a/TEST3-1.TXT", + "/a/TEST3-2.txt", + "/a/Test3-12.TXT", + "/a/Test3.TXT" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_SENSITIVE, + "/Test2.txt/a", + "/Test2_1.txt/a", + "/Test2_12.txt/a", + "/Test2_2.txt/b", + "/Test2_22.txt/b" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_SENSITIVE, + "/A/Test1.txt_2", + "/A/Test1.txt_22", + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_12" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_SENSITIVE, + "/B/Test1.txt_2", + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_12", + "/b/Test1.txt_22" + ); + } + + public void testCaseInsensitiveOrder() { + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_INSENSITIVE, + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_12", + "/a/Test1.txt_2", + "/a/Test1.txt_22" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_INSENSITIVE, + "/a/TEST3-1.TXT", + "/a/Test3-12.TXT", + "/a/TEST3-2.txt", + "/a/Test3.TXT" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_INSENSITIVE, + "/Test2.txt/a", + "/Test2_1.txt/a", + "/Test2_12.txt/a", + "/Test2_2.txt/b", + "/Test2_22.txt/b" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_INSENSITIVE, + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_12", + "/A/Test1.txt_2", + "/A/Test1.txt_22" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_INSENSITIVE, + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_12", + "/B/Test1.txt_2", + "/b/Test1.txt_22" + ); + + assertOrderedPaths( + HierarchicalFilePathComparator.CASE_INSENSITIVE, + "/A/B/a", + "/a/b/a", + "/a/b/a_2", + "/A/B/a_2", + "/A/B/a_3", + "/a/b/a_3" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.CASE_INSENSITIVE, + "/A/B/a_1", + "/a/b/a_2", + "/A/B_1/a_1", + "/a/b_1/a_2", + "/A/B_2/a_1", + "/a/b_2/a_2" + ); + } + + public void testNaturalOrder() { + assertStrictOrderedPaths( + HierarchicalFilePathComparator.NATURAL, + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_2", + "/a/Test1.txt_12", + "/a/Test1.txt_22" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.NATURAL, + "/a/Test3.TXT", + "/a/TEST3-1.TXT", + "/a/TEST3-2.txt", + "/a/Test3-12.TXT" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.NATURAL, + "/Test2.txt/a", + "/Test2_1.txt/a", + "/Test2_2.txt/b", + "/Test2_12.txt/a", + "/Test2_22.txt/b" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.NATURAL, + "/a/Test1.txt", + "/a/Test1.txt_1", + "/A/Test1.txt_2", + "/a/Test1.txt_12", + "/A/Test1.txt_22" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.NATURAL, + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_12", + "/B/Test1.txt_2", + "/b/Test1.txt_22" + ); + + assertStrictOrderedPaths( + HierarchicalFilePathComparator.NATURAL, + "/Test2.1.txt/a", + "/Test2.1.txt/b", + "/Test2_1.txt/a", + "/Test2_1.txt/b" + ); + } + public void testTransitive() { - Collection paths = ContainerUtil.list( + List paths = ContainerUtil.list( "", "/", "~", @@ -143,43 +310,105 @@ public class HierarchicalFilePathComparatorTest extends TestCase { "/folder/aaA/.gitignore", "/folder/Aaa-qwerty/", "/folder/aAa-qwerty/qwerty", - "/folder/aAa/qwerty/" + "/folder/aAa/qwerty/", + "/Test1", + "/TEST1", + "/Test1.txt", + "/Test1.TXT_1", + "/Test1.txt_2", + "/Test1.txt_12", + "/Test1.TXT_22", + "/Test1.TXT 1", + "/Test1.txt 2", + "/Test1.txt 12", + "/Test1.TXT 22", + "/Test1.txt-1", + "/Test1.TXT-2", + "/Test1.txt-12", + "/Test1.TXT-22", + "/Test1_1.txt", + "/Test1_2.txt", + "/Test1_12.txt", + "/TEST1 1.txt", + "/TEST1 2.txt", + "/Test1 12.txt", + "/Test1.1.txt", + "/Test1-2.txt", + "/Test1-12.txt", + "/a/Test1.txt", + "/a/Test1.txt_1", + "/a/Test1.txt_12", + "/A/Test1.txt", + "/A/Test1.txt_1", + "/A/Test1.txt_12", + "/B/Test1.txt_2", + "/b/Test1.txt_22", + "/b/Test1.txt_2", + "/B/Test1.txt_22", + "/Test1 12.txt/a/", + "/Test1.1.txt/a/", + "/Test1-2.txt/a/", + "/Test1-12.txt/a/", + "/Test1 12.txt/A/", + "/Test1.1.txt/A/", + "/Test1-2.txt/A/", + "/Test1-12.txt/A/", + "/Test1 12.txt/b/", + "/Test1.1.txt/B/", + "/Test1-2.txt/b/", + "/Test1-12.txt/B/" ); - List filePaths = ContainerUtil.map(paths, it -> filePath(it)); - assertComparisonContractNotViolated(filePaths, true); - assertComparisonContractNotViolated(filePaths, false); + assertComparisonContractNotViolated(filePaths, HierarchicalFilePathComparator.NATURAL); + assertComparisonContractNotViolated(filePaths, HierarchicalFilePathComparator.CASE_SENSITIVE); + assertComparisonContractNotViolated(filePaths, HierarchicalFilePathComparator.CASE_INSENSITIVE); } - private static void assertComparisonContractNotViolated(@NotNull List paths, boolean ignoreCase) { - PlatformTestUtil.assertComparisonContractNotViolated(paths, - (path1, path2) -> compare(path1, path2, ignoreCase), - (path1, path2) -> equals(path1, path2, ignoreCase)); + private static void assertStrictOrderedPaths(@NotNull Comparator comparator, @NotNull String... paths) { + assertOrderedPaths(Arrays.asList(paths), comparator, true); } - private static boolean equals(@NotNull FilePath path1, @NotNull FilePath path2, boolean ignoreCase) { + private static void assertOrderedPaths(@NotNull Comparator comparator, @NotNull String... paths) { + assertOrderedPaths(Arrays.asList(paths), comparator, false); + } + + private static void assertOrderedPaths(@NotNull List paths, @NotNull Comparator comparator, boolean strict) { + List filePaths = ContainerUtil.map(paths, it -> filePath(it)); + List sortedFilePaths = ContainerUtil.sorted(filePaths, comparator); + + if (strict) { + TreeSet pathsSet = new TreeSet<>(comparator); + pathsSet.addAll(filePaths); + assertEquals(filePaths.size(), pathsSet.size()); + } + + assertEquals(filePaths, sortedFilePaths); + assertComparisonContractNotViolated(filePaths, comparator); + } + + private static void assertComparisonContractNotViolated(@NotNull List filePaths, @NotNull Comparator comparator) { + PlatformTestUtil.assertComparisonContractNotViolated(filePaths, + (path1, path2) -> compare(path1, path2, comparator), + (path1, path2) -> equals(path1, path2, comparator)); + } + + private static boolean equals(@NotNull FilePath path1, @NotNull FilePath path2, @NotNull Comparator comparator) { if (path1.isDirectory() != path2.isDirectory()) return false; - return ignoreCase || !SystemInfo.isFileSystemCaseSensitive - ? StringUtil.equalsIgnoreCase(path1.getPath(), path2.getPath()) - : StringUtil.equals(path1.getPath(), path2.getPath()); + boolean ignoreCase = comparator == HierarchicalFilePathComparator.NATURAL || + comparator == HierarchicalFilePathComparator.CASE_INSENSITIVE; + return ignoreCase ? StringUtil.equalsIgnoreCase(path1.getPath(), path2.getPath()) + : StringUtil.equals(path1.getPath(), path2.getPath()); } private static int compare(@NotNull String path1, @NotNull String path2) { - int compare1 = compare(filePath(path1), filePath(path2)); - int compare2 = compare(filePath(path2), filePath(path1)); + int compare1 = compare(filePath(path1), filePath(path2), HierarchicalFilePathComparator.NATURAL); + int compare2 = compare(filePath(path2), filePath(path1), HierarchicalFilePathComparator.NATURAL); assert compare1 == -compare2; return compare1; } - private static int compare(FilePath path1, FilePath path2) { - return compare(path1, path2, true); - } - - private static int compare(FilePath path1, FilePath path2, boolean ignoreCase) { - HierarchicalFilePathComparator comparator = ignoreCase - ? HierarchicalFilePathComparator.IGNORE_CASE - : HierarchicalFilePathComparator.SYSTEM_CASE_SENSITIVE; + private static int compare(FilePath path1, FilePath path2, @NotNull Comparator comparator) { return Integer.signum(comparator.compare(path1, path2)); }