diff options
Diffstat (limited to 'lib/private/NaturalSort.php')
-rw-r--r-- | lib/private/NaturalSort.php | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/lib/private/NaturalSort.php b/lib/private/NaturalSort.php new file mode 100644 index 00000000000..f44e8032d36 --- /dev/null +++ b/lib/private/NaturalSort.php @@ -0,0 +1,139 @@ +<?php +/** + * @author AW-UC <git@a-wesemann.de> + * @author Lukas Reschke <lukas@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Robin McCorkell <robin@mccorkell.me.uk> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC; + +class NaturalSort { + private static $instance; + private $collator; + private $cache = array(); + + /** + * Instantiate a new \OC\NaturalSort instance. + * @param object $injectedCollator + */ + public function __construct($injectedCollator = null) { + // inject an instance of \Collator('en_US') to force using the php5-intl Collator + // or inject an instance of \OC\NaturalSort_DefaultCollator to force using Owncloud's default collator + if (isset($injectedCollator)) { + $this->collator = $injectedCollator; + \OCP\Util::writeLog('core', 'forced use of '.get_class($injectedCollator), \OCP\Util::DEBUG); + } + } + + /** + * 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 + if (isset($this->cache[$t])) { + return $this->cache[$t]; + } + $tz = array(); + $x = 0; + $y = -1; + $n = null; + + while (isset($t[$x])) { + $c = $t[$x]; + // only include the dot in strings + $m = ((!$n && $c === '.') || ($c >= '0' && $c <= '9')); + if ($m !== $n) { + // next chunk + $y++; + $tz[$y] = ''; + $n = $m; + } + $tz[$y] .= $c; + $x++; + } + $this->cache[$t] = $tz; + 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 string $a first string to compare + * @param string $b second string to compare + * @return int -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); + + for ($x = 0; isset($aa[$x]) && isset($bb[$x]); $x++) { + $aChunk = $aa[$x]; + $bChunk = $bb[$x]; + if ($aChunk !== $bChunk) { + // test first character (character comparison, not number comparison) + if ($aChunk[0] >= '0' && $aChunk[0] <= '9' && $bChunk[0] >= '0' && $bChunk[0] <= '9') { + $aNum = (int)$aChunk; + $bNum = (int)$bChunk; + return $aNum - $bNum; + } + return self::getCollator()->compare($aChunk, $bChunk); + } + } + return count($aa) - count($bb); + } + + /** + * Returns a singleton + * @return \OC\NaturalSort instance + */ + public static function getInstance() { + if (!isset(self::$instance)) { + self::$instance = new \OC\NaturalSort(); + } + return self::$instance; + } +} |