Merge pull request #1804 from nextcloud/newSorting

new sorting, adapted to server
This commit is contained in:
Mario Đanić 2017-11-24 20:47:23 +01:00 committed by GitHub
commit c52e33ab9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 183 additions and 47 deletions

View file

@ -1,2 +1,2 @@
DO NOT TOUCH; GENERATED BY DRONE
<span class="mdl-layout-title">Lint Report: 445 warnings</span>
<span class="mdl-layout-title">Lint Report: 444 warnings</span>

View file

@ -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)
{
if (result != 0) {
return result;
}
}
}
} else
{
result = thisChunk.compareTo(thatChunk);
} 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) {
return result;
}
}
}
} 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) {

View file

@ -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;
}
}