AlphanumComparator.java 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. * The Alphanum Algorithm is an improved sorting algorithm for strings
  3. * containing numbers. Instead of sorting numbers in ASCII order like
  4. * a standard sort, this algorithm sorts numbers in numeric order.
  5. *
  6. * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com
  7. *
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation; either
  12. * version 2.1 of the License, or any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. */
  24. package third_parties.daveKoeller;
  25. import com.owncloud.android.datamodel.OCFile;
  26. import java.io.File;
  27. import java.text.Collator;
  28. import java.util.Comparator;
  29. /*
  30. * This is an updated version with enhancements made by Daniel Migowski, Andre Bogus, and David Koelle
  31. * *
  32. * To convert to use Templates (Java 1.5+):
  33. * - Change "implements Comparator" to "implements Comparator<String>"
  34. * - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)"
  35. * - Remove the type checking and casting in compare().
  36. *
  37. * To use this class:
  38. * Use the static "sort" method from the java.util.Collections class:
  39. * Collections.sort(your list, new AlphanumComparator());
  40. *
  41. * Adapted to fit
  42. * https://github.com/nextcloud/server/blob/9a4253ef7c34f9dc71a6a9f7828a10df769f0c32/tests/lib/NaturalSortTest.php
  43. * by Tobias Kaminsky
  44. */
  45. public class AlphanumComparator<T> implements Comparator<T> {
  46. private boolean isDigit(char ch) {
  47. return ch >= 48 && ch <= 57;
  48. }
  49. private boolean isSpecialChar(char ch) {
  50. return ch <= 47 || ch >= 58 && ch <= 64 || ch >= 91 && ch <= 96 || ch >= 123 && ch <= 126;
  51. }
  52. /**
  53. * Length of string is passed in for improved efficiency (only need to calculate it once)
  54. **/
  55. private String getChunk(String string, int stringLength, int marker) {
  56. StringBuilder chunk = new StringBuilder();
  57. char c = string.charAt(marker);
  58. chunk.append(c);
  59. marker++;
  60. if (isDigit(c)) {
  61. while (marker < stringLength) {
  62. c = string.charAt(marker);
  63. if (!isDigit(c)) {
  64. break;
  65. }
  66. chunk.append(c);
  67. marker++;
  68. }
  69. } else if (!isSpecialChar(c)) {
  70. while (marker < stringLength) {
  71. c = string.charAt(marker);
  72. if (isDigit(c) || isSpecialChar(c)) {
  73. break;
  74. }
  75. chunk.append(c);
  76. marker++;
  77. }
  78. }
  79. return chunk.toString();
  80. }
  81. public int compare(OCFile o1, OCFile o2) {
  82. String s1 = o1.getRemotePath().toLowerCase();
  83. String s2 = o2.getRemotePath().toLowerCase();
  84. return compare(s1, s2);
  85. }
  86. public int compare(File f1, File f2) {
  87. String s1 = f1.getPath().toLowerCase();
  88. String s2 = f2.getPath().toLowerCase();
  89. return compare(s1, s2);
  90. }
  91. public int compare(T t1, T t2) {
  92. return compare(t1.toString(), t2.toString());
  93. }
  94. public int compare(String s1, String s2) {
  95. int thisMarker = 0;
  96. int thatMarker = 0;
  97. int s1Length = s1.length();
  98. int s2Length = s2.length();
  99. while (thisMarker < s1Length && thatMarker < s2Length) {
  100. String thisChunk = getChunk(s1, s1Length, thisMarker);
  101. thisMarker += thisChunk.length();
  102. String thatChunk = getChunk(s2, s2Length, thatMarker);
  103. thatMarker += thatChunk.length();
  104. // If both chunks contain numeric characters, sort them numerically
  105. int result = 0;
  106. if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
  107. // Simple chunk comparison by length.
  108. int thisChunkLength = thisChunk.length();
  109. result = thisChunkLength - thatChunk.length();
  110. // If equal, the first different number counts
  111. if (result == 0) {
  112. for (int i = 0; i < thisChunkLength; i++) {
  113. result = thisChunk.charAt(i) - thatChunk.charAt(i);
  114. if (result != 0) {
  115. return result;
  116. }
  117. }
  118. }
  119. } else if (isSpecialChar(thisChunk.charAt(0)) && isSpecialChar(thatChunk.charAt(0))) {
  120. for (int i = 0; i < thisChunk.length(); i++) {
  121. if (thisChunk.charAt(i) == '.') {
  122. return -1;
  123. } else if (thatChunk.charAt(i) == '.') {
  124. return 1;
  125. } else {
  126. result = thisChunk.charAt(i) - thatChunk.charAt(i);
  127. if (result != 0) {
  128. return result;
  129. }
  130. }
  131. }
  132. } else if (isSpecialChar(thisChunk.charAt(0)) && !isSpecialChar(thatChunk.charAt(0))) {
  133. return -1;
  134. } else if (!isSpecialChar(thisChunk.charAt(0)) && isSpecialChar(thatChunk.charAt(0))) {
  135. return 1;
  136. } else {
  137. result = Collator.getInstance().compare(thisChunk, thatChunk);
  138. }
  139. if (result != 0) {
  140. return result;
  141. }
  142. }
  143. return s1Length - s2Length;
  144. }
  145. }