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

Merge c18c0b3c69194ea0fc540e7b5dbb363b212a770b into c82dc892390744ef86bc55658b96cdd255a05132

Tobias Kaminsky 7 жил өмнө
parent
commit
9e10ea3aa8

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

@@ -23,55 +23,59 @@
  */
 
 package third_parties.daveKoeller;
-import java.io.File;
-import java.util.Comparator;
 
 import com.owncloud.android.datamodel.OCFile;
 
-/**
- * This is an updated version with enhancements made by Daniel Migowski,
- * Andre Bogus, and David Koelle
- *
+import java.io.File;
+import java.text.Collator;
+import java.util.Comparator;
+
+/*
+ * This is an updated version with enhancements made by Daniel Migowski, Andre Bogus, and David Koelle
+ *  * 
  * To convert to use Templates (Java 1.5+):
- *   - Change "implements Comparator" to "implements Comparator<String>"
- *   - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)"
- *   - Remove the type checking and casting in compare().
- *
+ * - Change "implements Comparator" to "implements Comparator<String>"
+ * - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)"
+ * - Remove the type checking and casting in compare().
+ * 
  * To use this class:
- *   Use the static "sort" method from the java.util.Collections class:
- *   Collections.sort(your list, new AlphanumComparator());
+ * Use the static "sort" method from the java.util.Collections class:
+ * Collections.sort(your list, new AlphanumComparator());
+ * 
+ * Adapted to fit 
+ * https://github.com/nextcloud/server/blob/9a4253ef7c34f9dc71a6a9f7828a10df769f0c32/tests/lib/NaturalSortTest.php
+ * by Tobias Kaminsky
  */
-public class AlphanumComparator implements Comparator<OCFile>
-{
-    private final boolean isDigit(char ch)
-    {
+public class AlphanumComparator<T> implements Comparator<T> {
+    private boolean isDigit(char ch) {
         return ch >= 48 && ch <= 57;
     }
 
-    /** Length of string is passed in for improved efficiency (only need to calculate it once) **/
-    private final String getChunk(String s, int slength, int marker)
-    {
+    private boolean isSpecialChar(char ch) {
+        return ch <= 47 || ch >= 58 && ch <= 64 || ch >= 91 && ch <= 96 || ch >= 123 && ch <= 126;
+    }
+
+    /**
+     * Length of string is passed in for improved efficiency (only need to calculate it once)
+     **/
+    private String getChunk(String string, int stringLength, int marker) {
         StringBuilder chunk = new StringBuilder();
-        char c = s.charAt(marker);
+        char c = string.charAt(marker);
         chunk.append(c);
         marker++;
-        if (isDigit(c))
-        {
-            while (marker < slength)
-            {
-                c = s.charAt(marker);
+        if (isDigit(c)) {
+            while (marker < stringLength) {
+                c = string.charAt(marker);
                 if (!isDigit(c)) {
                     break;
                 }
                 chunk.append(c);
                 marker++;
             }
-        } else
-        {
-            while (marker < slength)
-            {
-                c = s.charAt(marker);
-                if (isDigit(c)) {
+        } else if (!isSpecialChar(c)) {
+            while (marker < stringLength) {
+                c = string.charAt(marker);
+                if (isDigit(c) || isSpecialChar(c)) {
                     break;
                 }
                 chunk.append(c);
@@ -81,28 +85,31 @@ public class AlphanumComparator implements Comparator<OCFile>
         return chunk.toString();
     }
 
-    public int compare(OCFile o1, OCFile o2){
+    public int compare(OCFile o1, OCFile o2) {
         String s1 = o1.getRemotePath().toLowerCase();
         String s2 = o2.getRemotePath().toLowerCase();
 
         return compare(s1, s2);
     }
 
-    public int compare(File f1, File f2){
+    public int compare(File f1, File f2) {
         String s1 = f1.getPath().toLowerCase();
         String s2 = f2.getPath().toLowerCase();
 
         return compare(s1, s2);
     }
 
+    public int compare(T t1, T t2) {
+        return compare(t1.toString(), t2.toString());
+    }
+
     public int compare(String s1, String s2) {
         int thisMarker = 0;
         int thatMarker = 0;
         int s1Length = s1.length();
         int s2Length = s2.length();
 
-        while (thisMarker < s1Length && thatMarker < s2Length)
-        {
+        while (thisMarker < s1Length && thatMarker < s2Length) {
             String thisChunk = getChunk(s1, s1Length, thisMarker);
             thisMarker += thisChunk.length();
 
@@ -111,26 +118,38 @@ public class AlphanumComparator implements Comparator<OCFile>
 
             // If both chunks contain numeric characters, sort them numerically
             int result = 0;
-            if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(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++)
-                    {
+                if (result == 0) {
+                    for (int i = 0; i < thisChunkLength; i++) {
+                        result = thisChunk.charAt(i) - thatChunk.charAt(i);
+                        if (result != 0) {
+                            return result;
+                        }
+                    }
+                }
+            } else if (isSpecialChar(thisChunk.charAt(0)) && isSpecialChar(thatChunk.charAt(0))) {
+                for (int i = 0; i < thisChunk.length(); i++) {
+                    if (thisChunk.charAt(i) == '.') {
+                        return -1;
+                    } else if (thatChunk.charAt(i) == '.') {
+                        return 1;
+                    } else {
                         result = thisChunk.charAt(i) - thatChunk.charAt(i);
-                        if (result != 0)
-                        {
+                        if (result != 0) {
                             return result;
                         }
                     }
                 }
-            } else
-            {
-                result = thisChunk.compareTo(thatChunk);
+            } else if (isSpecialChar(thisChunk.charAt(0)) && !isSpecialChar(thatChunk.charAt(0))) {
+                return -1;
+            } else if (!isSpecialChar(thisChunk.charAt(0)) && isSpecialChar(thatChunk.charAt(0))) {
+                return 1;
+            } else {
+                result = Collator.getInstance().compare(thisChunk, thatChunk);
             }
 
             if (result != 0) {

+ 117 - 0
src/test/java/com/owncloud/android/utils/TestSorting.java

@@ -0,0 +1,117 @@
+package com.owncloud.android.utils;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import third_parties.daveKoeller.AlphanumComparator;
+
+import static org.junit.Assert.assertTrue;
+
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2017 Tobias Kaminsky
+ * Copyright (C) 2017 Nextcloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ Tests used from 
+ https://github.com/nextcloud/server/blob/9a4253ef7c34f9dc71a6a9f7828a10df769f0c32/tests/lib/NaturalSortTest.php
+ at 2017-11-21 to stay in sync with server.
+ Added first test with special chars
+ */
+
+public class TestSorting {
+
+    @Test
+    public void testSpecialChars() {
+
+        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));
+    }
+
+    @Test
+    public void testDifferentCasing() {
+        String[] unsortedArray = {"aaa", "bbb", "BBB", "AAA"};
+        String[] sortedArray = {"aaa", "AAA", "bbb", "BBB"};
+
+        assertTrue(sortAndTest(unsortedArray, 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"};
+
+        assertTrue(sortAndTest(unsortedArray, 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"};
+
+        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(unsortedArray, sortedArray));
+    }
+
+    @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"};
+
+        assertTrue(sortAndTest(unsortedArray, sortedArray));
+    }
+
+    private boolean sortAndTest(String[] unsortedArray, String[] sortedArray) {
+        List<String> unsortedList = Arrays.asList(unsortedArray);
+        List<String> sortedList = Arrays.asList(sortedArray);
+
+
+        Collections.sort(unsortedList, new AlphanumComparator());
+
+        for (int i = 0; i < sortedList.size(); i++) {
+            if (sortedList.get(i).compareTo(unsortedList.get(i)) != 0) {
+
+                System.out.println(" target: " + sortedList.toString());
+                System.out.println(" actual: " + unsortedList.toString());
+
+
+                return false;
+            }
+        }
+
+        return true;
+    }
+}