naturalCompare for sorting in "natural/human" sort order

This commit is contained in:
Bas Leijdekkers
2011-08-19 15:25:54 +02:00
parent 354b3c6cd2
commit 3557e4b43c
2 changed files with 71 additions and 2 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1959,6 +1959,58 @@ public class StringUtil {
return res;
}
/**
* Implementation of "Sorting for Humans: Natural Sort Order":
* http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html
*/
public static int naturalCompare(String string1, String string2) {
return naturalCompare(string1, string2, false);
}
private static int naturalCompare(String string1, String string2, boolean caseSensitive) {
final int string1Length = string1.length();
final int string2Length = string2.length();
for(int i = 0, j = 0; i < string1Length && j < string2Length; i++, j++) {
final char ch1 = string1.charAt(i);
final char ch2 = string2.charAt(j);
if (isDigit(ch1) && isDigit(ch2)) {
int startNum1 = i;
int startNum2 = j;
i++;
j++;
while (i < string1Length && isDigit(string1.charAt(i))) i++;
while (j < string2Length && isDigit(string2.charAt(j))) j++;
final int difference = Integer.parseInt(string1.substring(startNum1, i)) -
Integer.parseInt(string2.substring(startNum2, j));
if (difference != 0) {
return difference;
}
} else {
if (caseSensitive) {
return ch1 - ch2;
} else {
// similar logic to charsMatch() below
final int diff1 = Character.toUpperCase(ch1) - Character.toUpperCase(ch2);
if (diff1 != 0) {
final int diff2 = Character.toLowerCase(ch1) - Character.toLowerCase(ch2);
if (diff2 != 0) {
return diff1;
}
}
}
}
}
if (!caseSensitive && string1Length == string2Length) {
// do case sensitive compare if case insensitive strings are equal
return naturalCompare(string1, string2, true);
}
return string1Length - string2Length;
}
public static boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
public static int compare(@Nullable String s1, @Nullable String s2, boolean ignoreCase) {
if (s1 == s2) return 0;
if (s1 == null) return -1;
@@ -18,7 +18,7 @@ package com.intellij.util.text;
import com.intellij.openapi.util.text.StringUtil;
import junit.framework.TestCase;
import java.util.List;
import java.util.*;
/**
* @author Eugene Zhuravlev
@@ -70,4 +70,21 @@ public class StringUtilTest extends TestCase {
assertFalse(StringUtil.startsWithConcatenationOf("something", "something", "."));
assertFalse(StringUtil.startsWithConcatenationOf("some", "something", ""));
}
public void testNaturalCompare() {
final List<String> strings = new ArrayList(Arrays.asList("Test99", "tes0", "test0", "testing", "test", "test99", "test011", "test1",
"test 3", "test2", "test10a", "test10", "1.2.10.5", "1.2.9.1"));
final Comparator<String> c = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return StringUtil.naturalCompare(o1, o2);
}
};
Collections.sort(strings, c);
assertEquals(Arrays.asList("1.2.9.1", "1.2.10.5", "tes0", "test", "test 3", "test0", "test1", "test2", "test10", "test10a",
"test011", "Test99", "test99", "testing"), strings);
final List<String> strings2 = new ArrayList(Arrays.asList("t1", "T2", "T1", "t2"));
Collections.sort(strings2, c);
assertEquals(Arrays.asList("T1", "t1", "T2", "t2"), strings2);
}
}