PIM: implement sorting with boost::locale
This provides the sorting as described in the API. Using the phonebook collation is left for later.
This commit is contained in:
parent
2bf7a7359d
commit
a9ba05dada
|
@ -28,18 +28,141 @@
|
|||
|
||||
SE_BEGIN_CXX
|
||||
|
||||
/**
|
||||
* Use higher levels to break ties between strings which are
|
||||
* considered equal at the lower levels. For example, "Façade" and
|
||||
* "facade" would be compared as equal when using only base
|
||||
* characters, but compare differently when also considering a higher
|
||||
* level which includes accents.
|
||||
*
|
||||
* The drawback of higher levels is that they are computationally more
|
||||
* expensive (transformation is slower and leads to longer transformed
|
||||
* strings, thus a longer string comparisons during compare) and may
|
||||
* end up comparing aspects that are irrelevant (like case).
|
||||
*
|
||||
* The default here pays attention to accents, but ignores the case.
|
||||
*/
|
||||
static const boost::locale::collator_base::level_type DEFAULT_COLLATION_LEVEL = boost::locale::collator_base::secondary;
|
||||
|
||||
class CompareFirstLastBoost : public IndividualCompare {
|
||||
std::locale m_locale;
|
||||
const boost::locale::collator<char> &m_collator;
|
||||
public:
|
||||
CompareFirstLastBoost(const std::locale &locale) :
|
||||
m_locale(locale),
|
||||
m_collator(std::use_facet< boost::locale::collator<char> >(m_locale))
|
||||
{}
|
||||
|
||||
virtual void createCriteria(FolksIndividual *individual, Criteria_t &criteria) const {
|
||||
FolksStructuredName *fn =
|
||||
folks_name_details_get_structured_name(FOLKS_NAME_DETAILS(individual));
|
||||
if (fn) {
|
||||
const char *family = folks_structured_name_get_family_name(fn);
|
||||
const char *given = folks_structured_name_get_given_name(fn);
|
||||
criteria.push_back(given ? m_collator.transform(DEFAULT_COLLATION_LEVEL, given) : "");
|
||||
criteria.push_back(family ? m_collator.transform(DEFAULT_COLLATION_LEVEL, family) : "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CompareLastFirstBoost : public IndividualCompare {
|
||||
std::locale m_locale;
|
||||
const boost::locale::collator<char> &m_collator;
|
||||
public:
|
||||
CompareLastFirstBoost(const std::locale &locale) :
|
||||
m_locale(locale),
|
||||
m_collator(std::use_facet< boost::locale::collator<char> >(m_locale))
|
||||
{}
|
||||
|
||||
virtual void createCriteria(FolksIndividual *individual, Criteria_t &criteria) const {
|
||||
FolksStructuredName *fn =
|
||||
folks_name_details_get_structured_name(FOLKS_NAME_DETAILS(individual));
|
||||
if (fn) {
|
||||
const char *family = folks_structured_name_get_family_name(fn);
|
||||
const char *given = folks_structured_name_get_given_name(fn);
|
||||
criteria.push_back(family ? m_collator.transform(DEFAULT_COLLATION_LEVEL, family) : "");
|
||||
criteria.push_back(given ? m_collator.transform(DEFAULT_COLLATION_LEVEL, given) : "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CompareFullnameBoost : public IndividualCompare {
|
||||
std::locale m_locale;
|
||||
const boost::locale::collator<char> &m_collator;
|
||||
public:
|
||||
CompareFullnameBoost(const std::locale &locale) :
|
||||
m_locale(locale),
|
||||
m_collator(std::use_facet< boost::locale::collator<char> >(m_locale))
|
||||
{}
|
||||
|
||||
virtual void createCriteria(FolksIndividual *individual, Criteria_t &criteria) const {
|
||||
const char *fullname = folks_name_details_get_full_name(FOLKS_NAME_DETAILS(individual));
|
||||
if (fullname) {
|
||||
criteria.push_back(m_collator.transform(DEFAULT_COLLATION_LEVEL, fullname));
|
||||
} else {
|
||||
FolksStructuredName *fn =
|
||||
folks_name_details_get_structured_name(FOLKS_NAME_DETAILS(individual));
|
||||
if (fn) {
|
||||
const char *given = folks_structured_name_get_given_name(fn);
|
||||
const char *middle = folks_structured_name_get_additional_names(fn);
|
||||
const char *family = folks_structured_name_get_family_name(fn);
|
||||
const char *suffix = folks_structured_name_get_suffixes(fn);
|
||||
std::string buffer;
|
||||
buffer.reserve(256);
|
||||
#define APPEND(_str) \
|
||||
if (_str && *_str) { \
|
||||
if (!buffer.empty()) { \
|
||||
buffer += _str; \
|
||||
} \
|
||||
}
|
||||
APPEND(given);
|
||||
APPEND(middle);
|
||||
APPEND(family);
|
||||
APPEND(suffix);
|
||||
#undef APPEND
|
||||
criteria.push_back(m_collator.transform(DEFAULT_COLLATION_LEVEL, buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class LocaleFactoryBoost : public LocaleFactory
|
||||
{
|
||||
std::locale m_locale;
|
||||
|
||||
public:
|
||||
LocaleFactoryBoost() :
|
||||
m_locale(boost::locale::generator()(""))
|
||||
m_locale(genLocale())
|
||||
{}
|
||||
|
||||
static std::locale genLocale()
|
||||
{
|
||||
// Get current locale from environment. Configure the
|
||||
// generated locale so that it supports what we need and
|
||||
// nothing more.
|
||||
boost::locale::generator gen;
|
||||
gen.characters(boost::locale::char_facet);
|
||||
gen.categories(boost::locale::collation_facet |
|
||||
boost::locale::convert_facet |
|
||||
boost::locale::information_facet);
|
||||
// TODO: Check env vars, then append @collation=phonebook
|
||||
// to get phonebook specific sorting.
|
||||
return gen("");
|
||||
}
|
||||
|
||||
virtual boost::shared_ptr<IndividualCompare> createCompare(const std::string &order)
|
||||
{
|
||||
SE_THROW("not implemented");
|
||||
boost::shared_ptr<IndividualCompare> res;
|
||||
if (order == "first/last") {
|
||||
res.reset(new CompareFirstLastBoost(m_locale));
|
||||
} else if (order == "last/first") {
|
||||
res.reset(new CompareLastFirstBoost(m_locale));
|
||||
} else if (order == "fullname") {
|
||||
res.reset(new CompareFullnameBoost(m_locale));
|
||||
} else {
|
||||
SE_THROW("boost locale factory: sort order '" + order + "' not supported");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual boost::shared_ptr<IndividualFilter> createFilter(const StringMap &filter)
|
||||
|
|
Loading…
Reference in New Issue