LT Project: Sorting
For Bibliographic records, we order by author first, then title; for "raw" display we order by title. We manage this by means of a simple strategy.
class IRecordSortStrategy
{
public:
virtual ~IRecordSortStrategy();
virtual bool isLessThan(const IMinimalLibraryBookRecord &inRec1,
const IMinimalLibraryBookRecord &inRec2) const = 0;
};
(The IMinimalLibraryBookRecord reduces the dependence on the larger ILibraryBookRecord (which has several other dependencies) for the puposes of some simple classes which don't need the full dependency:
class IMinimalLibraryBookRecord
{
public:
virtual ~IMinimalLibraryBookRecord();
virtual Author getAuthor() const = 0;
virtual std::string_view getTitle() const = 0;
virtual int getTitleSortOffset() const = 0;
};
All three of the above correspond to fields provided by the LT data as downloaded.)
The basic sort strategy uses the title only:
class TitleRecordSortStrategy : public IRecordSortStrategy
{
public:
TitleRecordSortStrategy(const Log::ILogger &inLogger) : m_logger(inLogger) {}
~TitleRecordSortStrategy() override;
bool isLessThan(const IMinimalLibraryBookRecord &inRec1,
const IMinimalLibraryBookRecord &inRec2) const override;
private:
const Log::ILogger &m_logger;
};
Most of the space in the implementation is taken up with error handling: I had a certain amount of difficulty with earlier versions -- this function uncovered some issues in other classes -- and the exception handling is what is left over from that. The error handling not only takes up the space in the catch blocks but drives the unrolling of the title offset logic, so that all the values are available for logging.
bool TitleRecordSortStrategy::isLessThan(
const IMinimalLibraryBookRecord &inRec1,
const IMinimalLibraryBookRecord &inRec2) const
{
std::string_view s1 = inRec1.getTitle();
std::string_view s2 = inRec2.getTitle();
if (s1.empty())
{
return !s2.empty();
}
else
{
if (s2.empty())
return false;
else
{
int offset1 = inRec1.getTitleSortOffset();
if ((offset1 > 0)
&& (s1.length() > static_cast<std::size_t>(offset1)))
s1.remove_prefix(offset1);
int offset2 = inRec2.getTitleSortOffset();
if ((offset2 > 0)
&& (s2.length() > static_cast<std::size_t>(offset2)))
s2.remove_prefix(offset2);
try
{
return s1 < s2;
}
catch (const std::system_error &e)
{
std::ostringstream str;
str << e.what() << ":" << s1 << "," << offset1 << "; " << s2
<< "," << offset2;
m_logger.log(Log::Severity::Fatal, str.str(), e.code());
return false;
}
catch (const std::exception &ex)
{
std::ostringstream str;
str << ex.what() << ":" << s1 << "," << offset1 << "; " << s2
<< "," << offset2;
m_logger.log(Log::Severity::Fatal, str.str());
return false;
}
}
}
}
The Bibliographic strategy piggybacks on the Title Strategy:
class BibliographicRecordSortStrategy : public TitleRecordSortStrategy
{
public:
BibliographicRecordSortStrategy(const Log::ILogger &inLogger)
: TitleRecordSortStrategy(inLogger)
{ }
~BibliographicRecordSortStrategy() override;
bool isLessThan(const IMinimalLibraryBookRecord &inRec1,
const IMinimalLibraryBookRecord &inRec2) const override;
};
bool BibliographicRecordSortStrategy::isLessThan(
const IMinimalLibraryBookRecord &inRec1,
const IMinimalLibraryBookRecord &inRec2) const
{
auto s1 = inRec1.getAuthor();
auto s2 = inRec2.getAuthor();
if (s1.empty())
{
return !s2.empty();
}
else
{
if (s2.empty())
return false;
else
{
auto rval = s1.compare(s2);
if (rval != 0)
return (rval < 0);
else
return TitleRecordSortStrategy::isLessThan(inRec1, inRec2);
}
}
}
Essentially, compare using author keys; if they compare equal, use the inherited title comparison.
Comments
Post a Comment