Эх сурвалжийг харах

Merge pull request #2262 from nextcloud/naturalSorting

Add natural sorting
Andy Scherzinger 7 жил өмнө
parent
commit
aa1f5f8166

+ 15 - 3
src/main/java/com/owncloud/android/datamodel/OCFile.java

@@ -28,6 +28,7 @@ import android.content.Context;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.NonNull;
 import android.support.v4.content.FileProvider;
 
 import com.owncloud.android.R;
@@ -36,7 +37,6 @@ import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.utils.MimeType;
 
 import java.io.File;
-import java.util.Locale;
 
 import third_parties.daveKoeller.AlphanumComparator;
 
@@ -269,6 +269,18 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         return mMimeType != null && mMimeType.equals(MimeType.DIRECTORY);
     }
 
+
+    /**
+     * Sets mimetype to folder and returns this file
+     * Only for testing
+     *
+     * @return OCFile this file
+     */
+    public OCFile setFolder() {
+        setMimetype(MimeType.DIRECTORY);
+        return this;
+    }
+
     /**
      * Use this to check if this file is available locally
      *
@@ -618,9 +630,9 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
     }
 
     @Override
-    public int compareTo(OCFile another) {
+    public int compareTo(@NonNull OCFile another) {
         if (isFolder() && another.isFolder()) {
-            return getRemotePath().toLowerCase(Locale.ROOT).compareTo(another.getRemotePath().toLowerCase(Locale.ROOT));
+            return new AlphanumComparator().compare(this, another);
         } else if (isFolder()) {
             return -1;
         } else if (another.isFolder()) {

+ 46 - 15
src/main/java/third_parties/daveKoeller/AlphanumComparator.java

@@ -27,9 +27,9 @@ package third_parties.daveKoeller;
 import com.owncloud.android.datamodel.OCFile;
 
 import java.io.File;
+import java.io.Serializable;
 import java.text.Collator;
 import java.util.Comparator;
-import java.util.Locale;
 
 /*
  * This is an updated version with enhancements made by Daniel Migowski, Andre Bogus, and David Koelle
@@ -47,7 +47,7 @@ import java.util.Locale;
  * https://github.com/nextcloud/server/blob/9a4253ef7c34f9dc71a6a9f7828a10df769f0c32/tests/lib/NaturalSortTest.php
  * by Tobias Kaminsky
  */
-public class AlphanumComparator<T> implements Comparator<T> {
+public class AlphanumComparator<T> implements Comparator<T>, Serializable {
     private boolean isDigit(char ch) {
         return ch >= 48 && ch <= 57;
     }
@@ -87,15 +87,15 @@ public class AlphanumComparator<T> implements Comparator<T> {
     }
 
     public int compare(OCFile o1, OCFile o2) {
-        String s1 = o1.getRemotePath().toLowerCase(Locale.ROOT);
-        String s2 = o2.getRemotePath().toLowerCase(Locale.ROOT);
+        String s1 = o1.getFileName();
+        String s2 = o2.getFileName();
 
         return compare(s1, s2);
     }
 
     public int compare(File f1, File f2) {
-        String s1 = f1.getPath().toLowerCase(Locale.ROOT);
-        String s2 = f2.getPath().toLowerCase(Locale.ROOT);
+        String s1 = f1.getPath();
+        String s2 = f2.getPath();
 
         return compare(s1, s2);
     }
@@ -120,17 +120,48 @@ public class AlphanumComparator<T> implements Comparator<T> {
             // If both chunks contain numeric characters, sort them numerically
             int result = 0;
             if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
-                // Simple chunk comparison by length.
-                int thisChunkLength = thisChunk.length();
-                result = thisChunkLength - thatChunk.length();
-                // If equal, the first different number counts
-                if (result == 0) {
-                    for (int i = 0; i < thisChunkLength; i++) {
-                        result = thisChunk.charAt(i) - thatChunk.charAt(i);
-                        if (result != 0) {
-                            return result;
+                // extract digits
+                int thisChunkZeroCount = 0;
+                boolean zero = true;
+                int c = 0;
+                while (c < (thisChunk.length()) && isDigit(thisChunk.charAt(c))) {
+                    if (zero) {
+                        if (Character.getNumericValue(thisChunk.charAt(c)) == 0) {
+                            thisChunkZeroCount++;
+                        } else {
+                            zero = false;
+                        }
+                    }
+                    c++;
+                }
+                int thisChunkValue = Integer.parseInt(thisChunk.substring(0, c));
+
+                int thatChunkZeroCount = 0;
+                c = 0;
+                zero = true;
+                while (c < (thatChunk.length()) && isDigit(thatChunk.charAt(c))) {
+                    if (zero) {
+                        if (Character.getNumericValue(thatChunk.charAt(c)) == 0) {
+                            thatChunkZeroCount++;
+                        } else {
+                            zero = false;
                         }
                     }
+                    c++;
+                }
+                int thatChunkValue = Integer.parseInt(thatChunk.substring(0, c));
+
+                result = Integer.compare(thisChunkValue, thatChunkValue);
+                
+                if (result == 0) {
+                    // value is equal, compare leading zeros
+                    result = Integer.compare(thisChunkZeroCount, thatChunkZeroCount);
+
+                    if (result != 0) {
+                        return result;
+                    }
+                } else {
+                    return result;
                 }
             } else if (isSpecialChar(thisChunk.charAt(0)) && isSpecialChar(thatChunk.charAt(0))) {
                 for (int i = 0; i < thisChunk.length(); i++) {

+ 98 - 38
src/test/java/com/owncloud/android/utils/TestSorting.java

@@ -1,9 +1,13 @@
 package com.owncloud.android.utils;
 
+import com.owncloud.android.datamodel.OCFile;
+
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import third_parties.daveKoeller.AlphanumComparator;
@@ -42,71 +46,127 @@ public class TestSorting {
 
     @Test
     public void testSpecialChars() {
+        String[] sortedArray = {"[Test] Folder", "01 - January", "11 - November", "Ôle",
+                "Test 1", "Test 01", "Test 04", "Üüü",
+                "z.[Test], z. Test"};
 
-        String[] unsortedArray = {"11 - November", "Test 04", "Test 01", "Ôle", "Üüü", "01 - Januar", "[Test] Folder",
-                "z.[Test]", "z. Test"};
-
-        String[] sortedArray = {"[Test] Folder", "01 - Januar", "11 - November", "Ôle", "Test 01", "Test 04", "Üüü",
-                "z. Test", "z.[Test]"};
-
-        assertTrue(sortAndTest(unsortedArray, sortedArray));
+        assertTrue(sortAndTest(Arrays.asList(sortedArray)));
     }
 
     @Test
     public void testDifferentCasing() {
-        String[] unsortedArray = {"aaa", "bbb", "BBB", "AAA"};
         String[] sortedArray = {"aaa", "AAA", "bbb", "BBB"};
 
-        assertTrue(sortAndTest(unsortedArray, sortedArray));
+        assertTrue(sortAndTest(Arrays.asList(sortedArray)));
     }
 
     @Test
-    public void testNumbers() {
-        String[] unsortedArray = {"124.txt", "abc1", "123.txt", "abc", "abc2", "def (2).txt", "ghi 10.txt", "abc12",
-                "def.txt", "def (1).txt", "ghi 2.txt", "def (10).txt", "abc10", "def (12).txt", "z", "ghi.txt", "za",
-                "ghi 1.txt", "ghi 12.txt", "zz", "15.txt", "15b.txt"};
-
-        String[] sortedArray = {"15.txt", "15b.txt", "123.txt", "124.txt", "abc", "abc1", "abc2", "abc10", "abc12",
-                "def.txt", "def (1).txt", "def (2).txt", "def (10).txt", "def (12).txt", "ghi.txt", "ghi 1.txt",
-                "ghi 2.txt", "ghi 10.txt", "ghi 12.txt", "z", "za", "zz"};
+    public void testLeadingZeros() {
+        String[] sortedArray = {"T 0 abc", "T 00 abc", "T 000 abc", "T 1 abc", "T 01 abc",
+                "T 001 abc", "T 2 abc", "T 02 abc", "T 3 abc", "T 03 abc"};
 
-        assertTrue(sortAndTest(unsortedArray, sortedArray));
+        assertTrue(sortAndTest(Arrays.asList(sortedArray)));
     }
 
     @Test
-    public void testChineseCharacters() {
-        String[] unsortedArray = {"十.txt", "一.txt", "二.txt", "十 2.txt", "三.txt", "四.txt", "abc.txt", "五.txt",
-                "七.txt", "八.txt", "九.txt", "六.txt", "十一.txt", "波.txt", "破.txt", "莫.txt", "啊.txt", "123.txt"};
+    public void testTrailingDigits() {
+        String[] unsortedArray = {"Zeros 2", "Zeros", "T 2", "T", "T 01", "T 003", "A"};
+        String[] sortedArray = {"A", "T", "T 01", "T 2", "T 003", "Zeros", "Zeros 2"};
 
-        String[] sortedArray = {"123.txt", "abc.txt", "一.txt", "七.txt", "三.txt", "九.txt", "二.txt", "五.txt",
-                "八.txt", "六.txt", "十.txt", "十 2.txt", "十一.txt", "啊.txt", "四.txt", "波.txt", "破.txt", "莫.txt"};
+        assertTrue(sortAndTest(Arrays.asList(sortedArray)));
+    }
 
-        assertTrue(sortAndTest(unsortedArray, sortedArray));
+    @Test
+    public void testOCFilesWithFolderFirst() {
+        List<OCFile> sortedArray = new ArrayList<>();
+        sortedArray.add(new OCFile("/ah.txt").setFolder());
+        sortedArray.add(new OCFile("/Äh.txt").setFolder());
+        sortedArray.add(new OCFile("/oh.txt").setFolder());
+        sortedArray.add(new OCFile("/öh.txt").setFolder());
+        sortedArray.add(new OCFile("/üh.txt").setFolder());
+        sortedArray.add(new OCFile("/Üh.txt").setFolder());
+        sortedArray.add(new OCFile("/äh.txt"));
+        sortedArray.add(new OCFile("/Öh.txt"));
+        sortedArray.add(new OCFile("/uh.txt"));
+        sortedArray.add(new OCFile("/Üh 2.txt"));
+
+        assertTrue(sortAndTest(sortedArray));
     }
 
+    /**
+     * uses OCFile.compareTo() instead of custom comparator
+     */
     @Test
-    public void testWithUmlauts() {
-        String[] unsortedArray = {"öh.txt", "Äh.txt", "oh.txt", "Üh 2.txt", "Üh.txt", "ah.txt", "Öh.txt", "uh.txt",
-                "üh.txt", "äh.txt"};
-        String[] sortedArray = {"ah.txt", "äh.txt", "Äh.txt", "oh.txt", "öh.txt", "Öh.txt", "uh.txt", "üh.txt",
-                "Üh.txt", "Üh 2.txt"};
+    public void testOCFiles() {
+        List<OCFile> sortedArray = new ArrayList<>();
+        sortedArray.add(new OCFile("/ah.txt").setFolder());
+        sortedArray.add(new OCFile("/Äh.txt").setFolder());
+        sortedArray.add(new OCFile("/oh.txt").setFolder());
+        sortedArray.add(new OCFile("/öh.txt").setFolder());
+        sortedArray.add(new OCFile("/üh.txt").setFolder());
+        sortedArray.add(new OCFile("/Üh.txt").setFolder());
+        sortedArray.add(new OCFile("/äh.txt"));
+        sortedArray.add(new OCFile("/Öh.txt"));
+        sortedArray.add(new OCFile("/uh.txt"));
+        sortedArray.add(new OCFile("/Üh 2.txt"));
+
+        List unsortedList = shuffle(sortedArray);
+        Collections.sort(unsortedList);
+
+        assertTrue(test(sortedArray, unsortedList));
+    }
+
+    private List<Comparable> shuffle(List<? extends Comparable> files) {
+        List<Comparable> shuffled = new ArrayList<>();
+        shuffled.addAll(files);
 
-        assertTrue(sortAndTest(unsortedArray, sortedArray));
+        Collections.shuffle(shuffled);
+
+        return shuffled;
     }
 
-    private boolean sortAndTest(String[] unsortedArray, String[] sortedArray) {
-        List<String> unsortedList = Arrays.asList(unsortedArray);
-        List<String> sortedList = Arrays.asList(sortedArray);
+    private boolean sortAndTest(List<? extends Comparable> sortedList) {
+        return test(sortedList, sort(sortedList));
+    }
 
+    private List<Comparable> sort(List<? extends Comparable> sortedList) {
+        List unsortedList = shuffle(sortedList);
+
+        if (sortedList.get(0) instanceof OCFile) {
+            Collections.sort(unsortedList, (Comparator<OCFile>) (o1, o2) -> {
+                if (o1.isFolder() && o2.isFolder()) {
+                    return new AlphanumComparator().compare(o1, o2);
+                } else if (o1.isFolder()) {
+                    return -1;
+                } else if (o2.isFolder()) {
+                    return 1;
+                }
+                return new AlphanumComparator().compare(o1, o2);
+            });
+        } else {
+            Collections.sort(unsortedList, new AlphanumComparator<>());
+        }
 
-        Collections.sort(unsortedList, new AlphanumComparator());
+        return unsortedList;
+    }
 
-        for (int i = 0; i < sortedList.size(); i++) {
-            if (sortedList.get(i).compareTo(unsortedList.get(i)) != 0) {
+    private boolean test(List<? extends Comparable> target, List<? extends Comparable> actual) {
+
+        for (int i = 0; i < target.size(); i++) {
+            int compare;
+
+            if (target.get(i) instanceof OCFile) {
+                String sortedName = ((OCFile) target.get(i)).getFileName();
+                String unsortedName = ((OCFile) actual.get(i)).getFileName();
+                compare = sortedName.compareTo(unsortedName);
+            } else {
+                compare = target.get(i).compareTo(actual.get(i));
+            }
 
-                System.out.println(" target: " + sortedList.toString());
-                System.out.println(" actual: " + unsortedList.toString());
+            if (compare != 0) {
 
+                System.out.println(" target: \n" + target.toString());
+                System.out.println(" actual: \n" + actual.toString());
 
                 return false;
             }