vector<pair<CinemaID, Cinema>>
CinemaList::cinemas() const
{
- SQLiteStatement statement(_db, _cinemas.select("ORDER BY name ASC"));
+ SQLiteStatement statement(_db, _cinemas.select("ORDER BY name COLLATE unicode ASC"));
return cinemas_from_result(statement);
}
#include <unicode/utypes.h>
#include <unicode/usearch.h>
#include <unicode/ustring.h>
+#include <fmt/format.h>
#include <boost/scoped_array.hpp>
#include <cstring>
#include <vector>
using std::vector;
-Collator::Collator(char const* locale)
+Collator::Collator()
{
UErrorCode status = U_ZERO_ERROR;
- _collator = ucol_open(locale, &status);
+#ifdef DCPOMATIC_POSIX
+ _collator = ucol_open("POSIX", &status);
+#else
+ _collator = ucol_open(nullptr, &status);
+#endif
if (_collator) {
ucol_setAttribute(_collator, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
/* Ignore case and character encoding (and probably some other things) */
Collator::compare (string const& utf8_a, string const& utf8_b) const
{
if (_collator) {
- auto utf16_a = utf8_to_utf16(utf8_a);
- auto utf16_b = utf8_to_utf16(utf8_b);
- return ucol_strcoll(_collator, utf16_a.data(), -1, utf16_b.data(), -1);
+ UErrorCode error = U_ZERO_ERROR;
+ auto const result = ucol_strcollUTF8(_collator, utf8_a.data(), -1, utf8_b.data(), -1, &error);
+ if (error != U_ZERO_ERROR) {
+ throw std::runtime_error(fmt::format("Failed to compare strings ({})", static_cast<int>(error)));
+ }
+ return result;
} else {
return strcoll(utf8_a.c_str(), utf8_b.c_str());
}
class Collator
{
public:
- Collator(char const* locale = nullptr);
+ Collator();
~Collator();
Collator(Collator const &) = delete;
vector<std::pair<DKDMRecipientID, DKDMRecipient>>
DKDMRecipientList::dkdm_recipients() const
{
- SQLiteStatement statement(_db, _dkdm_recipients.select("ORDER BY name ASC"));
+ SQLiteStatement statement(_db, _dkdm_recipients.select("ORDER BY name COLLATE unicode ASC"));
return dkdm_recipients_from_result(statement);
}
#include <sqlite3.h>
+using std::string;
+
+
+static
+int collator_compare(void* context, int length_a, const void* string_a, int length_b, const void* string_b)
+{
+ auto collator = reinterpret_cast<Collator*>(context);
+ return collator->compare(
+ string(reinterpret_cast<char const*>(string_a), length_a),
+ string(reinterpret_cast<char const*>(string_b), length_b)
+ );
+}
+
+
SQLiteDatabase::SQLiteDatabase(boost::filesystem::path path)
{
#ifdef DCPOMATIC_WINDOWS
}
sqlite3_busy_timeout(_db, 500);
+
+ rc = sqlite3_create_collation(_db, "unicode", SQLITE_UTF8, &_collator, collator_compare);
+ if (rc != SQLITE_OK) {
+ throw std::runtime_error("Could not set SQLite database collation");
+ }
}
#define DCPOMATIC_SQLITE_DATABASE_H
+#include "collator.h"
#include <boost/filesystem.hpp>
struct sqlite3;
private:
sqlite3* _db = nullptr;
+ Collator _collator;
};
BOOST_REQUIRE_EQUAL(cinemas.size(), 3U);
auto cinema_iter = cinemas.begin();
+ BOOST_CHECK_EQUAL(cinema_iter->second.name, "classy joint");
+ BOOST_CHECK_EQUAL(cinema_iter->second.notes, "Can't stand this place");
+ ++cinema_iter;
+
BOOST_CHECK_EQUAL(cinema_iter->second.name, "Great");
BOOST_CHECK_EQUAL(cinema_iter->second.emails.size(), 1U);
BOOST_CHECK_EQUAL(cinema_iter->second.emails[0], "julie@tinyscreen.com");
BOOST_CHECK(cinema_iter->second.utc_offset == dcp::UTCOffset(1, 0));
++cinema_iter;
- BOOST_CHECK_EQUAL(cinema_iter->second.name, "classy joint");
- BOOST_CHECK_EQUAL(cinema_iter->second.notes, "Can't stand this place");
- ++cinema_iter;
-
BOOST_CHECK_EQUAL(cinema_iter->second.name, "stinking dump");
BOOST_CHECK_EQUAL(cinema_iter->second.emails.size(), 2U);
BOOST_CHECK_EQUAL(cinema_iter->second.emails[0], "bob@odourscreen.com");
BOOST_CHECK_EQUAL(screen_iter->second.recipient()->subject_dn_qualifier(), "CVsuuv9eYsQZSl8U4fDpvOmzZhI=");
}
+
+BOOST_AUTO_TEST_CASE(cinemas_list_sort_test)
+{
+ auto const db = setup("cinemas_list_sort_test");
+
+ CinemaList cinemas(db);
+ cinemas.add_cinema({"Ŝpecial", { "foo@bar.com" }, "", dcp::UTCOffset()});
+ cinemas.add_cinema({"Ţest", { "foo@bar.com" }, "", dcp::UTCOffset()});
+ cinemas.add_cinema({"Name", { "foo@bar.com" }, "", dcp::UTCOffset()});
+ cinemas.add_cinema({"ÄBC", { "foo@bar.com" }, "", dcp::UTCOffset()});
+
+ auto sorted = cinemas.cinemas();
+ BOOST_REQUIRE_EQUAL(sorted.size(), 4U);
+ BOOST_CHECK_EQUAL(sorted[0].second.name, "ÄBC");
+ BOOST_CHECK_EQUAL(sorted[1].second.name, "Name");
+ BOOST_CHECK_EQUAL(sorted[2].second.name, "Ŝpecial");
+ BOOST_CHECK_EQUAL(sorted[3].second.name, "Ţest");
+}
+
BOOST_AUTO_TEST_CASE(collator_compare_works_and_ignores_case)
{
- Collator collator("en");
+ Collator collator;
#if 0
// Print out available locales
BOOST_AUTO_TEST_CASE(collator_search_works_and_ignores_case)
{
- Collator collator("en");
+ Collator collator;
BOOST_CHECK(collator.find("outh", "With filthy mouths, and bad attitudes"));
BOOST_CHECK(collator.find("with", "With filthy mouths, and bad attitudes"));
/* The detailed creation of sqlite3 from XML is tested in cinema_list_test.cc */
auto cinemas = test.cinemas();
BOOST_REQUIRE_EQUAL(cinemas.size(), 3U);
- BOOST_CHECK_EQUAL(cinemas[0].second.name, "Great");
- BOOST_CHECK_EQUAL(cinemas[1].second.name, "classy joint");
+ BOOST_CHECK_EQUAL(cinemas[0].second.name, "classy joint");
+ BOOST_CHECK_EQUAL(cinemas[1].second.name, "Great");
BOOST_CHECK_EQUAL(cinemas[2].second.name, "stinking dump");
/* Add another recipient to the sqlite */
auto cinemas = test.cinemas();
BOOST_REQUIRE_EQUAL(cinemas.size(), 4U);
- BOOST_CHECK_EQUAL(cinemas[0].second.name, "Great");
- BOOST_CHECK_EQUAL(cinemas[1].second.name, "The ol' 1-seater");
- BOOST_CHECK_EQUAL(cinemas[2].second.name, "classy joint");
- BOOST_CHECK_EQUAL(cinemas[3].second.name, "stinking dump");
+ BOOST_CHECK_EQUAL(cinemas[0].second.name, "classy joint");
+ BOOST_CHECK_EQUAL(cinemas[1].second.name, "Great");
+ BOOST_CHECK_EQUAL(cinemas[2].second.name, "stinking dump");
+ BOOST_CHECK_EQUAL(cinemas[3].second.name, "The ol' 1-seater");
}
}
CinemaList cinema_list(cinemas_file);
auto cinemas = cinema_list.cinemas();
BOOST_REQUIRE_EQUAL(cinemas.size(), 3U);
- BOOST_CHECK_EQUAL(cinemas[0].second.name, "Great");
- BOOST_CHECK_EQUAL(cinemas[1].second.name, "classy joint");
+ BOOST_CHECK_EQUAL(cinemas[0].second.name, "classy joint");
+ BOOST_CHECK_EQUAL(cinemas[1].second.name, "Great");
BOOST_CHECK_EQUAL(cinemas[2].second.name, "stinking dump");
}
CinemaList cinema_list("build/test/hide/it/here/cinemas.sqlite3");
auto cinemas = cinema_list.cinemas();
BOOST_REQUIRE_EQUAL(cinemas.size(), 3U);
- BOOST_CHECK_EQUAL(cinemas[0].second.name, "Great");
- BOOST_CHECK_EQUAL(cinemas[1].second.name, "classy joint");
+ BOOST_CHECK_EQUAL(cinemas[0].second.name, "classy joint");
+ BOOST_CHECK_EQUAL(cinemas[1].second.name, "Great");
BOOST_CHECK_EQUAL(cinemas[2].second.name, "stinking dump");
}
BOOST_CHECK(!error);
BOOST_REQUIRE_EQUAL(output.size(), 3U);
- BOOST_CHECK_EQUAL(output[0], "Great (julie@tinyscreen.com)");
- BOOST_CHECK_EQUAL(output[1], "classy joint ()");
+ BOOST_CHECK_EQUAL(output[0], "classy joint ()");
+ BOOST_CHECK_EQUAL(output[1], "Great (julie@tinyscreen.com)");
BOOST_CHECK_EQUAL(output[2], "stinking dump (bob@odourscreen.com, alice@whiff.com)");
}