From 173059f6d00faa06dab9188efb2d7536f15861e4 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 18 Feb 2014 12:29:05 +0100 Subject: Fixed file list sorting Now using a natural sort algorithm that is more consistent between JS and PHP (although not perfect in some corner cases) - added OC.Util.naturalSortComparator that uses the same algo that was used for the user list - changed user list and files list to use OC.Util.naturalSortComparator - removed toLowerCase() and changed the comparator to use String.localeCompare() - added unit tests - added OC_NaturalSort that is used by OCP\Util::naturalSortCompare() --- lib/private/naturalsort.php | 116 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 lib/private/naturalsort.php (limited to 'lib/private/naturalsort.php') diff --git a/lib/private/naturalsort.php b/lib/private/naturalsort.php new file mode 100644 index 00000000000..b6fa0ed8968 --- /dev/null +++ b/lib/private/naturalsort.php @@ -0,0 +1,116 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ + +namespace OC; + +class NaturalSort_DefaultCollator { + + public function compare($a, $b) { + if ($a === $b) { + return 0; + } + return ($a < $b) ? -1 : 1; + } +} + +class NaturalSort { + private static $instance; + private $collator; + + /** + * Split the given string in chunks of numbers and strings + * @param string $t string + * @return array of strings and number chunks + */ + private function naturalSortChunkify($t) { + // Adapted and ported to PHP from + // http://my.opera.com/GreyWyvern/blog/show.dml/1671288 + $tz = array(); + $x = 0; + $y = -1; + $n = null; + $length = strlen($t); + + while ($x < $length) { + $c = $t[$x]; + $m = ($c === '.' || ($c >= '0' && $c <= '9')); + if ($m !== $n) { + // next chunk + $y++; + $tz[$y] = ''; + $n = $m; + } + $tz[$y] .= $c; + $x++; + } + return $tz; + } + + /** + * Returns the string collator + * @return Collator string collator + */ + private function getCollator() { + if (!isset($this->collator)) { + // looks like the default is en_US_POSIX which yields wrong sorting with + // German umlauts, so using en_US instead + if (class_exists('Collator')) { + $this->collator = new \Collator('en_US'); + } + else { + $this->collator = new \OC\NaturalSort_DefaultCollator(); + } + } + return $this->collator; + } + + /** + * Compare two strings to provide a natural sort + * @param $a first string to compare + * @param $b second string to compare + * @return -1 if $b comes before $a, 1 if $a comes before $b + * or 0 if the strings are identical + */ + public function compare($a, $b) { + // Needed because PHP doesn't sort correctly when numbers are enclosed in + // parenthesis, even with NUMERIC_COLLATION enabled. + // For example it gave ["test (2).txt", "test.txt"] + // instead of ["test.txt", "test (2).txt"] + $aa = self::naturalSortChunkify($a); + $bb = self::naturalSortChunkify($b); + $alen = count($aa); + $blen = count($bb); + + for ($x = 0; $x < $alen && $x < $blen; $x++) { + $aChunk = $aa[$x]; + $bChunk = $bb[$x]; + if ($aChunk !== $bChunk) { + if (is_numeric($aChunk) && is_numeric($bChunk)) { + $aNum = (int)$aChunk; + $bNum = (int)$bChunk; + return $aNum - $bNum; + } + return self::getCollator()->compare($aChunk, $bChunk); + } + } + return $alen - $blen; + } + + /** + * Returns a singleton + * @return \OC\NaturalSort instance + */ + public static function getInstance() { + if (!isset(self::$instance)) { + self::$instance = new \OC\NaturalSort(); + } + return self::$instance; + } +} + -- cgit v1.2.3