summaryrefslogtreecommitdiffstats
path: root/fbreader/src/library
diff options
context:
space:
mode:
authorMichele Calgaro <michele.calgaro@yahoo.it>2024-05-11 21:28:48 +0900
committerMichele Calgaro <michele.calgaro@yahoo.it>2024-05-11 21:28:48 +0900
commit2462d03f322261bd616721c2b2065c4004b36c9c (patch)
tree239947a0737bb8386703a1497f12c09aebd3080a /fbreader/src/library
downloadtde-ebook-reader-2462d03f322261bd616721c2b2065c4004b36c9c.tar.gz
tde-ebook-reader-2462d03f322261bd616721c2b2065c4004b36c9c.zip
Initial import (as is) from Debian Snapshot's 'fbreader' source code (https://snapshot.debian.org/package/fbreader/0.99.4%2Bdfsg-6).
The Debian code is provided under GPL2 license. Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'fbreader/src/library')
-rw-r--r--fbreader/src/library/Author.cpp67
-rw-r--r--fbreader/src/library/Author.h71
-rw-r--r--fbreader/src/library/Book.cpp280
-rw-r--r--fbreader/src/library/Book.h142
-rw-r--r--fbreader/src/library/Comparators.cpp106
-rw-r--r--fbreader/src/library/Library.cpp439
-rw-r--r--fbreader/src/library/Library.h126
-rw-r--r--fbreader/src/library/Lists.h39
-rw-r--r--fbreader/src/library/Number.cpp64
-rw-r--r--fbreader/src/library/Number.h45
-rw-r--r--fbreader/src/library/Tag.cpp139
-rw-r--r--fbreader/src/library/Tag.h98
12 files changed, 1616 insertions, 0 deletions
diff --git a/fbreader/src/library/Author.cpp b/fbreader/src/library/Author.cpp
new file mode 100644
index 0000000..2478bdd
--- /dev/null
+++ b/fbreader/src/library/Author.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <ZLUnicodeUtil.h>
+
+#include "Author.h"
+
+std::set<shared_ptr<Author>,AuthorComparator> Author::ourAuthorSet;
+
+shared_ptr<Author> Author::getAuthor(const std::string &name, const std::string &sortKey) {
+ std::string strippedName = name;
+ ZLUnicodeUtil::utf8Trim(strippedName);
+ if (strippedName.empty()) {
+ return 0;
+ }
+ std::string strippedKey = sortKey;
+ ZLUnicodeUtil::utf8Trim(strippedKey);
+
+ if (strippedKey.empty()) {
+ const std::size_t index = strippedName.find(',');
+ if (index != std::string::npos) {
+ strippedKey = strippedName.substr(0, index);
+ ZLUnicodeUtil::utf8Trim(strippedKey);
+ }
+ }
+
+ if (strippedKey.empty()) {
+ std::size_t index = strippedName.rfind(' ');
+ if (index == std::string::npos) {
+ strippedKey = strippedName;
+ } else {
+ strippedKey = strippedName.substr(index + 1);
+ const std::size_t size = strippedName.size();
+ while (index < size && strippedName[index] == ' ') {
+ --index;
+ }
+ strippedName = strippedName.substr(0, index + 1) + ' ' + strippedKey;
+ }
+ }
+
+ shared_ptr<Author> author =
+ new Author(strippedName, ZLUnicodeUtil::toLower(strippedKey));
+ std::set<shared_ptr<Author>,AuthorComparator>::const_iterator it =
+ ourAuthorSet.find(author);
+ if (it != ourAuthorSet.end()) {
+ return *it;
+ } else {
+ ourAuthorSet.insert(author);
+ return author;
+ }
+}
diff --git a/fbreader/src/library/Author.h b/fbreader/src/library/Author.h
new file mode 100644
index 0000000..04907f8
--- /dev/null
+++ b/fbreader/src/library/Author.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __AUTHOR_H__
+#define __AUTHOR_H__
+
+#include <string>
+#include <map>
+#include <set>
+
+#include <shared_ptr.h>
+
+#include "Lists.h"
+
+class Author;
+
+class AuthorComparator {
+
+public:
+ bool operator () (
+ const shared_ptr<Author> author0,
+ const shared_ptr<Author> author1
+ ) const;
+};
+
+class Author {
+
+private:
+ static std::set<shared_ptr<Author>,AuthorComparator> ourAuthorSet;
+
+public:
+ static shared_ptr<Author> getAuthor(const std::string &name, const std::string &sortKey = "");
+
+private:
+ Author(const std::string &name, const std::string &sortkey);
+
+public:
+ const std::string &name() const;
+ const std::string &sortKey() const;
+
+private:
+ const std::string myName;
+ const std::string mySortKey;
+
+private: // disable copying:
+ Author(const Author &);
+ const Author &operator = (const Author &);
+};
+
+inline Author::Author(const std::string &name, const std::string &sortkey) : myName(name), mySortKey(sortkey) {}
+
+inline const std::string &Author::name() const { return myName; }
+inline const std::string &Author::sortKey() const { return mySortKey; }
+
+#endif /* __AUTHOR_H__ */
diff --git a/fbreader/src/library/Book.cpp b/fbreader/src/library/Book.cpp
new file mode 100644
index 0000000..ca3afd0
--- /dev/null
+++ b/fbreader/src/library/Book.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <algorithm>
+#include <set>
+
+#include <ZLStringUtil.h>
+#include <ZLFile.h>
+#include <ZLLanguageList.h>
+
+#include "Book.h"
+#include "Author.h"
+#include "Tag.h"
+
+#include "../formats/FormatPlugin.h"
+#include "../migration/BookInfo.h"
+
+const std::string Book::AutoEncoding = "auto";
+
+Book::Book(const ZLFile &file, int id) : myBookId(id), myFile(file) {
+}
+
+Book::~Book() {
+}
+
+shared_ptr<Book> Book::createBook(
+ const ZLFile &file,
+ int id,
+ const std::string &encoding,
+ const std::string &language,
+ const std::string &title
+) {
+ Book *book = new Book(file, id);
+ book->setEncoding(encoding);
+ book->setLanguage(language);
+ book->setTitle(title);
+ return book;
+}
+
+shared_ptr<Book> Book::loadFromFile(const ZLFile &file) {
+ shared_ptr<FormatPlugin> plugin = PluginCollection::Instance().plugin(file, false);
+ if (plugin.isNull()) {
+ return 0;
+ }
+
+ shared_ptr<Book> book = new Book(file, 0);
+ if (!plugin->readMetaInfo(*book)) {
+ return 0;
+ }
+ plugin->readLanguageAndEncoding(*book);
+
+ if (book->title().empty()) {
+ book->setTitle(ZLFile::fileNameToUtf8(file.name(true)));
+ }
+
+ if (book->encoding().empty()) {
+ book->setEncoding(AutoEncoding);
+ }
+
+ if (book->language().empty()) {
+ book->setLanguage(PluginCollection::Instance().DefaultLanguageOption.value());
+ }
+
+ return book;
+}
+
+bool Book::addTag(shared_ptr<Tag> tag) {
+ if (tag.isNull()) {
+ return false;
+ }
+ TagList::const_iterator it = std::find(myTags.begin(), myTags.end(), tag);
+ if (it == myTags.end()) {
+ myTags.push_back(tag);
+ return true;
+ }
+ return false;
+}
+
+bool Book::addTag(const std::string &fullName) {
+ return addTag(Tag::getTagByFullName(fullName));
+}
+
+bool Book::removeTag(shared_ptr<Tag> tag, bool includeSubTags) {
+ bool changed = false;
+ for (TagList::iterator it = myTags.begin(); it != myTags.end();) {
+ if (tag == *it || (includeSubTags && tag->isAncestorOf(*it))) {
+ it = myTags.erase(it);
+ changed = true;
+ } else {
+ ++it;
+ }
+ }
+ return changed;
+}
+
+bool Book::renameTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) {
+ if (includeSubTags) {
+ std::set<shared_ptr<Tag> > tagSet;
+ bool changed = false;
+ for (TagList::const_iterator it = myTags.begin(); it != myTags.end(); ++it) {
+ if (*it == from) {
+ tagSet.insert(to);
+ changed = true;
+ } else {
+ shared_ptr<Tag> newtag = Tag::cloneSubTag(*it, from, to);
+ if (newtag.isNull()) {
+ tagSet.insert(*it);
+ } else {
+ tagSet.insert(newtag);
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ myTags.clear();
+ myTags.insert(myTags.end(), tagSet.begin(), tagSet.end());
+ return true;
+ }
+ } else {
+ TagList::iterator it = std::find(myTags.begin(), myTags.end(), from);
+ if (it != myTags.end()) {
+ TagList::const_iterator jt = std::find(myTags.begin(), myTags.end(), to);
+ if (jt == myTags.end()) {
+ *it = to;
+ } else {
+ myTags.erase(it);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Book::cloneTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) {
+ if (includeSubTags) {
+ std::set<shared_ptr<Tag> > tagSet;
+ for (TagList::const_iterator it = myTags.begin(); it != myTags.end(); ++it) {
+ if (*it == from) {
+ tagSet.insert(to);
+ } else {
+ shared_ptr<Tag> newtag = Tag::cloneSubTag(*it, from, to);
+ if (!newtag.isNull()) {
+ tagSet.insert(newtag);
+ }
+ }
+ }
+ if (!tagSet.empty()) {
+ tagSet.insert(myTags.begin(), myTags.end());
+ myTags.clear();
+ myTags.insert(myTags.end(), tagSet.begin(), tagSet.end());
+ return true;
+ }
+ } else {
+ TagList::const_iterator it = std::find(myTags.begin(), myTags.end(), from);
+ if (it != myTags.end()) {
+ TagList::const_iterator jt = std::find(myTags.begin(), myTags.end(), to);
+ if (jt == myTags.end()) {
+ myTags.push_back(to);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+shared_ptr<Book> Book::loadFromBookInfo(const ZLFile &file) {
+ BookInfo info(file.path());
+
+ shared_ptr<Book> book = createBook(
+ file, 0,
+ info.EncodingOption.value(),
+ info.LanguageOption.value(),
+ info.TitleOption.value()
+ );
+
+ book->setSeries(
+ info.SeriesTitleOption.value(),
+ Number(info.IndexInSeriesOption.value())
+ );
+
+ if (book->language().empty()) {
+ book->setLanguage(PluginCollection::Instance().DefaultLanguageOption.value());
+ }
+
+ const std::string &tagList = info.TagsOption.value();
+ if (!tagList.empty()) {
+ std::size_t index = 0;
+ do {
+ std::size_t newIndex = tagList.find(',', index);
+ book->addTag(Tag::getTagByFullName(tagList.substr(index, newIndex - index)));
+ index = newIndex + 1;
+ } while (index != 0);
+ }
+
+ const std::string &authorList = info.AuthorDisplayNameOption.value();
+ if (!authorList.empty()) {
+ std::size_t index = 0;
+ do {
+ std::size_t newIndex = authorList.find(',', index);
+ book->addAuthor(authorList.substr(index, newIndex - index));
+ index = newIndex + 1;
+ } while (index != 0);
+ }
+
+ return book;
+}
+
+bool Book::replaceAuthor(shared_ptr<Author> from, shared_ptr<Author> to) {
+ AuthorList::iterator it = std::find(myAuthors.begin(), myAuthors.end(), from);
+ if (it == myAuthors.end()) {
+ return false;
+ }
+ if (to.isNull()) {
+ myAuthors.erase(it);
+ } else {
+ *it = to;
+ }
+ return true;
+}
+
+void Book::setTitle(const std::string &title) {
+ myTitle = title;
+}
+
+void Book::setLanguage(const std::string &language) {
+ if (!myLanguage.empty()) {
+ const std::vector<std::string> &codes = ZLLanguageList::languageCodes();
+ std::vector<std::string>::const_iterator it =
+ std::find(codes.begin(), codes.end(), myLanguage);
+ std::vector<std::string>::const_iterator jt =
+ std::find(codes.begin(), codes.end(), language);
+ if (it != codes.end() && jt == codes.end()) {
+ return;
+ }
+ }
+ myLanguage = language;
+}
+
+void Book::setEncoding(const std::string &encoding) {
+ myEncoding = encoding;
+}
+
+void Book::setSeries(const std::string &title, const Number &index) {
+ mySeriesTitle = title;
+ myIndexInSeries = index;
+}
+
+void Book::removeAllTags() {
+ myTags.clear();
+}
+
+void Book::addAuthor(const std::string &displayName, const std::string &sortKey) {
+ addAuthor(Author::getAuthor(displayName, sortKey));
+}
+
+void Book::addAuthor(shared_ptr<Author> author) {
+ if (!author.isNull()) {
+ myAuthors.push_back(author);
+ }
+}
+
+void Book::removeAllAuthors() {
+ myAuthors.clear();
+}
diff --git a/fbreader/src/library/Book.h b/fbreader/src/library/Book.h
new file mode 100644
index 0000000..daed2e0
--- /dev/null
+++ b/fbreader/src/library/Book.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __BOOK_H__
+#define __BOOK_H__
+
+#include <string>
+
+#include <shared_ptr.h>
+
+#include <ZLFile.h>
+
+#include "Lists.h"
+#include "Number.h"
+
+class Author;
+class Tag;
+
+class Book {
+
+public:
+ static const std::string AutoEncoding;
+
+public:
+ static shared_ptr<Book> createBook(
+ const ZLFile &file,
+ int id,
+ const std::string &encoding,
+ const std::string &language,
+ const std::string &title
+ );
+
+ static shared_ptr<Book> loadFromFile(const ZLFile &file);
+
+ // this method is used in Migration only
+ static shared_ptr<Book> loadFromBookInfo(const ZLFile &file);
+
+private:
+ Book(const ZLFile &file, int id);
+
+public:
+ ~Book();
+
+public: // unmodifiable book methods
+ const std::string &title() const;
+ const ZLFile &file() const;
+ const std::string &language() const;
+ const std::string &encoding() const;
+ const std::string &seriesTitle() const;
+ const Number &indexInSeries() const;
+
+ const TagList &tags() const;
+ const AuthorList &authors() const;
+
+public: // modifiable book methods
+ void setTitle(const std::string &title);
+ void setLanguage(const std::string &language);
+ void setEncoding(const std::string &encoding);
+ void setSeries(const std::string &title, const Number &index);
+
+public:
+ bool addTag(shared_ptr<Tag> tag);
+ bool addTag(const std::string &fullName);
+ bool removeTag(shared_ptr<Tag> tag, bool includeSubTags);
+ bool renameTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags);
+ bool cloneTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags);
+ void removeAllTags();
+
+ void addAuthor(shared_ptr<Author> author);
+ void addAuthor(const std::string &displayName, const std::string &sortKey = std::string());
+ bool replaceAuthor(shared_ptr<Author> from, shared_ptr<Author> to);
+ void removeAllAuthors();
+
+public:
+ int bookId() const;
+ void setBookId(int bookId);
+
+private:
+ int myBookId;
+
+ const ZLFile myFile;
+ std::string myTitle;
+ std::string myLanguage;
+ std::string myEncoding;
+ std::string mySeriesTitle;
+ Number myIndexInSeries;
+ TagList myTags;
+ AuthorList myAuthors;
+
+private: // disable copying
+ Book(const Book &);
+ const Book &operator = (const Book &);
+};
+
+class BookComparator {
+
+public:
+ bool operator () (
+ const shared_ptr<Book> book0,
+ const shared_ptr<Book> book1
+ ) const;
+};
+
+class BookByFileNameComparator {
+
+public:
+ bool operator () (
+ const shared_ptr<Book> book0,
+ const shared_ptr<Book> book1
+ ) const;
+};
+
+inline const std::string &Book::title() const { return myTitle; }
+inline const ZLFile &Book::file() const { return myFile; }
+inline const std::string &Book::language() const { return myLanguage; }
+inline const std::string &Book::encoding() const { return myEncoding; }
+inline const std::string &Book::seriesTitle() const { return mySeriesTitle; }
+inline const Number &Book::indexInSeries() const { return myIndexInSeries; }
+
+inline const TagList &Book::tags() const { return myTags; }
+inline const AuthorList &Book::authors() const { return myAuthors; }
+
+inline int Book::bookId() const { return myBookId; }
+inline void Book::setBookId(int bookId) { myBookId = bookId; }
+
+#endif /* __BOOK_H__ */
diff --git a/fbreader/src/library/Comparators.cpp b/fbreader/src/library/Comparators.cpp
new file mode 100644
index 0000000..30b4059
--- /dev/null
+++ b/fbreader/src/library/Comparators.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <cstdlib>
+
+#include "Book.h"
+#include "Author.h"
+#include "Tag.h"
+
+bool BookComparator::operator() (
+ const shared_ptr<Book> book0,
+ const shared_ptr<Book> book1
+) const {
+ const std::string &seriesTitle0 = book0->seriesTitle();
+ const std::string &seriesTitle1 = book1->seriesTitle();
+ int comp = seriesTitle0.compare(seriesTitle1);
+ if (comp == 0) {
+ if (!seriesTitle0.empty()) {
+ if (book0->indexInSeries() < book1->indexInSeries() &&
+ !(book0->indexInSeries() == book1->indexInSeries())) {
+ return true;
+ }
+ }
+ return book0->title() < book1->title();
+ }
+ if (seriesTitle0.empty()) {
+ return book0->title() < seriesTitle1;
+ }
+ if (seriesTitle1.empty()) {
+ return seriesTitle0 <= book1->title();
+ }
+ return comp < 0;
+}
+
+bool BookByFileNameComparator::operator() (
+ const shared_ptr<Book> book0,
+ const shared_ptr<Book> book1
+) const {
+ return book0->file() < book1->file();
+}
+
+bool AuthorComparator::operator() (
+ const shared_ptr<Author> author0,
+ const shared_ptr<Author> author1
+) const {
+ if (author0.isNull()) {
+ return !author1.isNull();
+ }
+ if (author1.isNull()) {
+ return false;
+ }
+
+ const int comp = author0->sortKey().compare(author1->sortKey());
+ return comp != 0 ? comp < 0 : author0->name() < author1->name();
+}
+
+bool TagComparator::operator() (
+ shared_ptr<Tag> tag0,
+ shared_ptr<Tag> tag1
+) const {
+ if (tag0.isNull()) {
+ return !tag1.isNull();
+ }
+ if (tag1.isNull()) {
+ return false;
+ }
+
+ std::size_t level0 = tag0->level();
+ std::size_t level1 = tag1->level();
+ if (level0 > level1) {
+ for (; level0 > level1; --level0) {
+ tag0 = tag0->parent();
+ }
+ if (tag0 == tag1) {
+ return false;
+ }
+ } else if (level0 < level1) {
+ for (; level0 < level1; --level1) {
+ tag1 = tag1->parent();
+ }
+ if (tag0 == tag1) {
+ return true;
+ }
+ }
+ while (tag0->parent() != tag1->parent()) {
+ tag0 = tag0->parent();
+ tag1 = tag1->parent();
+ }
+ return tag0->name() < tag1->name();
+}
diff --git a/fbreader/src/library/Library.cpp b/fbreader/src/library/Library.cpp
new file mode 100644
index 0000000..8ee1f36
--- /dev/null
+++ b/fbreader/src/library/Library.cpp
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2004-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <queue>
+#include <algorithm>
+
+#include <ZLibrary.h>
+#include <ZLStringUtil.h>
+#include <ZLFile.h>
+#include <ZLDir.h>
+#include <ZLDialogManager.h>
+
+#include "Library.h"
+#include "Book.h"
+#include "Author.h"
+#include "Tag.h"
+
+#include "../formats/FormatPlugin.h"
+
+#include "../database/booksdb/BooksDBUtil.h"
+#include "../database/booksdb/BooksDB.h"
+
+shared_ptr<Library> Library::ourInstance;
+const std::size_t Library::MaxRecentListSize = 10;
+
+Library &Library::Instance() {
+ if (ourInstance.isNull()) {
+ ourInstance = new Library();
+ }
+ return *ourInstance;
+}
+
+static const std::string OPTIONS = "Options";
+
+Library::Library() :
+ PathOption(ZLCategoryKey::CONFIG, OPTIONS, "BookPath", ""),
+ ScanSubdirsOption(ZLCategoryKey::CONFIG, OPTIONS, "ScanSubdirs", true),
+ CollectAllBooksOption(ZLCategoryKey::CONFIG, OPTIONS, "CollectAllBooks", false),
+ myBuildMode(BUILD_ALL),
+ myRevision(0) {
+ BooksDBUtil::getRecentBooks(myRecentBooks);
+}
+
+void Library::collectBookFileNames(std::set<std::string> &bookFileNames) const {
+ std::set<std::string> dirs;
+ collectDirNames(dirs);
+
+ while (!dirs.empty()) {
+ std::string dirname = *dirs.begin();
+ dirs.erase(dirs.begin());
+
+ ZLFile dirfile(dirname);
+ std::vector<std::string> files;
+ bool inZip = false;
+
+ shared_ptr<ZLDir> dir = dirfile.directory();
+ if (dir.isNull()) {
+ continue;
+ }
+
+ if (dirfile.isArchive()) {
+ ZLFile phys(dirfile.physicalFilePath());
+ if (!BooksDBUtil::checkInfo(phys)) {
+ BooksDBUtil::resetZipInfo(phys);
+ BooksDBUtil::saveInfo(phys);
+ }
+ BooksDBUtil::listZipEntries(dirfile, files);
+ inZip = true;
+ } else {
+ dir->collectFiles(files, true);
+ }
+ if (!files.empty()) {
+ const bool collectBookWithoutMetaInfo = CollectAllBooksOption.value();
+ for (std::vector<std::string>::const_iterator jt = files.begin(); jt != files.end(); ++jt) {
+ const std::string fileName = (inZip) ? (*jt) : (dir->itemPath(*jt));
+ ZLFile file(fileName);
+ if (PluginCollection::Instance().plugin(file, !collectBookWithoutMetaInfo) != 0) {
+ bookFileNames.insert(fileName);
+ } else if (file.isArchive()) {
+ if (myScanSubdirs || !inZip) {
+ dirs.insert(fileName);
+ }
+ }
+ }
+ }
+ }
+}
+
+void Library::rebuildBookSet() const {
+ myBooks.clear();
+ myExternalBooks.clear();
+
+ std::map<std::string, shared_ptr<Book> > booksMap;
+ BooksDBUtil::getBooks(booksMap);
+
+ std::set<std::string> fileNamesSet;
+ collectBookFileNames(fileNamesSet);
+
+ // collect books from book path
+ for (std::set<std::string>::iterator it = fileNamesSet.begin(); it != fileNamesSet.end(); ++it) {
+ std::map<std::string, shared_ptr<Book> >::iterator jt = booksMap.find(*it);
+ if (jt == booksMap.end()) {
+ insertIntoBookSet(BooksDBUtil::getBook(*it));
+ } else {
+ insertIntoBookSet(jt->second);
+ booksMap.erase(jt);
+ }
+ }
+
+ // other books from our database
+ for (std::map<std::string, shared_ptr<Book> >::iterator jt = booksMap.begin(); jt != booksMap.end(); ++jt) {
+ shared_ptr<Book> book = jt->second;
+ if (!book.isNull()) {
+ if (BooksDB::Instance().checkBookList(*book)) {
+ insertIntoBookSet(book);
+ myExternalBooks.insert(book);
+ }
+ }
+ }
+}
+
+std::size_t Library::revision() const {
+ if (myBuildMode == BUILD_NOTHING &&
+ (myScanSubdirs != ScanSubdirsOption.value() ||
+ myPath != PathOption.value())) {
+ myPath = PathOption.value();
+ myScanSubdirs = ScanSubdirsOption.value();
+ myBuildMode = BUILD_ALL;
+ }
+
+ return (myBuildMode == BUILD_NOTHING) ? myRevision : myRevision + 1;
+}
+
+class LibrarySynchronizer : public DBRunnable {
+
+public:
+ LibrarySynchronizer(Library::BuildMode mode) : myBuildMode(mode) { }
+
+private:
+ bool run() {
+ Library &library = Library::Instance();
+
+ if (myBuildMode & Library::BUILD_COLLECT_FILES_INFO) {
+ library.rebuildBookSet();
+ }
+
+ if (myBuildMode & Library::BUILD_UPDATE_BOOKS_INFO) {
+ library.rebuildMaps();
+ }
+ return true;
+ }
+
+private:
+ const Library::BuildMode myBuildMode;
+};
+
+class LibrarySynchronizerWrapper : public ZLRunnable {
+
+public:
+ LibrarySynchronizerWrapper(Library::BuildMode mode) : myRunnable(mode) { }
+
+private:
+ void run() {
+ BooksDB::Instance().executeAsTransaction(myRunnable);
+ }
+
+private:
+ LibrarySynchronizer myRunnable;
+};
+
+void Library::synchronize() const {
+ if (myScanSubdirs != ScanSubdirsOption.value() ||
+ myPath != PathOption.value()) {
+ myPath = PathOption.value();
+ myScanSubdirs = ScanSubdirsOption.value();
+ myBuildMode = BUILD_ALL;
+ }
+
+ if (myBuildMode == BUILD_NOTHING) {
+ return;
+ }
+
+ LibrarySynchronizerWrapper synchronizer(myBuildMode);
+ myBuildMode = BUILD_NOTHING;
+ ZLDialogManager::Instance().wait(ZLResourceKey("loadingBookList"), synchronizer);
+
+ ++myRevision;
+}
+
+void Library::rebuildMaps() const {
+ myAuthors.clear();
+ myBooksByAuthor.clear();
+ myTags.clear();
+ myBooksByTag.clear();
+
+ for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) {
+ if ((*it).isNull()) {
+ continue;
+ }
+
+ const AuthorList &bookAuthors = (*it)->authors();
+ if (bookAuthors.empty()) {
+ myBooksByAuthor[0].push_back(*it);
+ } else {
+ for(AuthorList::const_iterator jt = bookAuthors.begin(); jt != bookAuthors.end(); ++jt) {
+ myBooksByAuthor[*jt].push_back(*it);
+ }
+ }
+
+ const TagList &bookTags = (*it)->tags();
+ if (bookTags.empty()) {
+ myBooksByTag[0].push_back(*it);
+ } else {
+ for(TagList::const_iterator kt = bookTags.begin(); kt != bookTags.end(); ++kt) {
+ myBooksByTag[*kt].push_back(*it);
+ }
+ }
+ }
+ for (BooksByAuthor::iterator mit = myBooksByAuthor.begin(); mit != myBooksByAuthor.end(); ++mit) {
+ myAuthors.push_back(mit->first);
+ std::sort(mit->second.begin(), mit->second.end(), BookComparator());
+ }
+ for (BooksByTag::iterator mjt = myBooksByTag.begin(); mjt != myBooksByTag.end(); ++mjt) {
+ myTags.push_back(mjt->first);
+ std::sort(mjt->second.begin(), mjt->second.end(), BookComparator());
+ }
+}
+
+void Library::collectDirNames(std::set<std::string> &nameSet) const {
+ std::queue<std::string> nameQueue;
+
+ std::string path = myPath;
+ int pos = path.find(ZLibrary::PathDelimiter);
+ while (pos != -1) {
+ nameQueue.push(path.substr(0, pos));
+ path.erase(0, pos + 1);
+ pos = path.find(ZLibrary::PathDelimiter);
+ }
+ if (!path.empty()) {
+ nameQueue.push(path);
+ }
+
+ std::set<std::string> resolvedNameSet;
+ while (!nameQueue.empty()) {
+ std::string name = nameQueue.front();
+ nameQueue.pop();
+ ZLFile f(name);
+ const std::string resolvedName = f.resolvedPath();
+ if (resolvedNameSet.find(resolvedName) == resolvedNameSet.end()) {
+ if (myScanSubdirs) {
+ shared_ptr<ZLDir> dir = f.directory();
+ if (!dir.isNull()) {
+ std::vector<std::string> subdirs;
+ dir->collectSubDirs(subdirs, true);
+ for (std::vector<std::string>::const_iterator it = subdirs.begin(); it != subdirs.end(); ++it) {
+ nameQueue.push(dir->itemPath(*it));
+ }
+ }
+ }
+ resolvedNameSet.insert(resolvedName);
+ nameSet.insert(name);
+ }
+ }
+}
+
+void Library::updateBook(shared_ptr<Book> book) {
+ BooksDB::Instance().saveBook(book);
+ myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO);
+}
+
+void Library::addBook(shared_ptr<Book> book) {
+ if (!book.isNull()) {
+ BooksDB::Instance().saveBook(book);
+ insertIntoBookSet(book);
+ myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO);
+ }
+}
+
+void Library::removeBook(shared_ptr<Book> book) {
+ if (!book.isNull()) {
+ BookSet::iterator it = myBooks.find(book);
+ if (it != myBooks.end()) {
+ myBooks.erase(it);
+ myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO);
+ }
+ BooksDB::Instance().deleteFromBookList(*book);
+ bool recentListChanged = false;
+ for (BookList::iterator it = myRecentBooks.begin(); it != myRecentBooks.end();) {
+ if ((*it)->file() == book->file()) {
+ it = myRecentBooks.erase(it);
+ recentListChanged = true;
+ } else {
+ ++it;
+ }
+ }
+ if (recentListChanged) {
+ BooksDB::Instance().saveRecentBooks(myRecentBooks);
+ }
+ }
+}
+
+const AuthorList &Library::authors() const {
+ synchronize();
+ return myAuthors;
+}
+
+const TagList &Library::tags() const {
+ synchronize();
+ return myTags;
+}
+
+const BookList &Library::books(shared_ptr<Author> author) const {
+ synchronize();
+ return myBooksByAuthor[author];
+}
+
+const BookList &Library::books(shared_ptr<Tag> tag) const {
+ synchronize();
+ return myBooksByTag[tag];
+}
+
+void Library::collectSeriesTitles(shared_ptr<Author> author, std::set<std::string> &titles) const {
+ const BookList &bookList = books(author);
+ for (BookList::const_iterator it = bookList.begin(); it != bookList.end(); ++it) {
+ const std::string &current = (*it)->seriesTitle();
+ if (!current.empty()) {
+ titles.insert(current);
+ }
+ }
+}
+
+void Library::removeTag(shared_ptr<Tag> tag, bool includeSubTags) {
+ for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) {
+ if ((*it)->removeTag(tag, includeSubTags)) {
+ BooksDB::Instance().saveTags(*it);
+ }
+ }
+ myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO);
+}
+
+void Library::renameTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) {
+ if (to != from) {
+ synchronize();
+ for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) {
+ BooksDBUtil::renameTag(*it, from, to, includeSubTags);
+ }
+ }
+ myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO);
+}
+
+void Library::cloneTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) {
+ if (to != from) {
+ synchronize();
+ for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) {
+ BooksDBUtil::cloneTag(*it, from, to, includeSubTags);
+ }
+ }
+ myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO);
+}
+
+bool Library::hasBooks(shared_ptr<Tag> tag) const {
+ synchronize();
+ const BooksByTag::const_iterator it = myBooksByTag.find(tag);
+ return it != myBooksByTag.end() && !it->second.empty();
+}
+
+bool Library::hasSubtags(shared_ptr<Tag> tag) const {
+ const TagList &tagList = tags();
+ const TagList::const_iterator it =
+ std::upper_bound(tagList.begin(), tagList.end(), tag, TagComparator());
+ return it != tagList.end() && tag->isAncestorOf(*it);
+}
+
+void Library::replaceAuthor(shared_ptr<Author> from, shared_ptr<Author> to) {
+ if (to != from) {
+ for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) {
+ if ((*it)->replaceAuthor(from, to)) {
+ BooksDB::Instance().saveAuthors(*it);
+ }
+ }
+ myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO);
+ }
+}
+
+Library::RemoveType Library::canRemove(shared_ptr<Book> book) const {
+ synchronize();
+ return (RemoveType)(
+ (myExternalBooks.find(book) != myExternalBooks.end() ?
+ REMOVE_FROM_LIBRARY : REMOVE_DONT_REMOVE) |
+ (BooksDBUtil::canRemoveFile(book->file().path()) ?
+ REMOVE_FROM_DISK : REMOVE_DONT_REMOVE)
+ );
+}
+
+void Library::insertIntoBookSet(shared_ptr<Book> book) const {
+ if (!book.isNull()) {
+ myBooks.insert(book);
+ }
+}
+
+const BookList &Library::recentBooks() const {
+ return myRecentBooks;
+}
+
+void Library::addBookToRecentList(shared_ptr<Book> book) {
+ if (book.isNull()) {
+ return;
+ }
+ for (BookList::iterator it = myRecentBooks.begin(); it != myRecentBooks.end(); ++it) {
+ if ((*it)->file() == book->file()) {
+ if (it == myRecentBooks.begin()) {
+ return;
+ }
+ myRecentBooks.erase(it);
+ break;
+ }
+ }
+ myRecentBooks.insert(myRecentBooks.begin(), book);
+ if (myRecentBooks.size() > MaxRecentListSize) {
+ myRecentBooks.erase(myRecentBooks.begin() + MaxRecentListSize, myRecentBooks.end());
+ }
+ BooksDB::Instance().saveRecentBooks(myRecentBooks);
+}
diff --git a/fbreader/src/library/Library.h b/fbreader/src/library/Library.h
new file mode 100644
index 0000000..86eda4b
--- /dev/null
+++ b/fbreader/src/library/Library.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2004-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __LIBRARY_H__
+#define __LIBRARY_H__
+
+#include <string>
+#include <vector>
+#include <set>
+#include <map>
+
+#include <shared_ptr.h>
+
+#include <ZLOptions.h>
+
+#include "Book.h"
+#include "Author.h"
+#include "Tag.h"
+#include "Lists.h"
+
+class Library {
+
+public:
+ static Library &Instance();
+
+private:
+ static shared_ptr<Library> ourInstance;
+ static const std::size_t MaxRecentListSize;
+
+public:
+ ZLStringOption PathOption;
+ ZLBooleanOption ScanSubdirsOption;
+ ZLBooleanOption CollectAllBooksOption;
+
+private:
+ Library();
+
+public:
+ const AuthorList &authors() const;
+ const TagList &tags() const;
+ const BookList &books(shared_ptr<Author> author) const;
+ const BookList &books(shared_ptr<Tag> tag) const;
+ const BookList &recentBooks() const;
+
+ enum RemoveType {
+ REMOVE_DONT_REMOVE = 0,
+ REMOVE_FROM_LIBRARY = 1,
+ REMOVE_FROM_DISK = 2,
+ REMOVE_FROM_LIBRARY_AND_DISK = REMOVE_FROM_LIBRARY | REMOVE_FROM_DISK
+ };
+
+ RemoveType canRemove(shared_ptr<Book> book) const;
+
+ void collectSeriesTitles(shared_ptr<Author> author, std::set<std::string> &titles) const;
+
+ std::size_t revision() const;
+
+ void addBook(shared_ptr<Book> book);
+ void removeBook(shared_ptr<Book> book);
+ void updateBook(shared_ptr<Book> book);
+ void addBookToRecentList(shared_ptr<Book> book);
+
+ void replaceAuthor(shared_ptr<Author> from, shared_ptr<Author> to);
+
+ bool hasBooks(shared_ptr<Tag> tag) const;
+ bool hasSubtags(shared_ptr<Tag> tag) const;
+ void removeTag(shared_ptr<Tag> tag, bool includeSubTags);
+ void renameTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags);
+ void cloneTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags);
+
+private:
+ void collectDirNames(std::set<std::string> &names) const;
+ void collectBookFileNames(std::set<std::string> &bookFileNames) const;
+
+ void synchronize() const;
+
+ void rebuildBookSet() const;
+ void rebuildMaps() const;
+
+ void insertIntoBookSet(shared_ptr<Book> book) const;
+
+private:
+ mutable BookSet myBooks;
+ mutable BookSet myExternalBooks;
+
+ mutable AuthorList myAuthors;
+ mutable TagList myTags;
+ typedef std::map<shared_ptr<Author>,BookList,AuthorComparator> BooksByAuthor;
+ mutable BooksByAuthor myBooksByAuthor;
+ typedef std::map<shared_ptr<Tag>,BookList,TagComparator> BooksByTag;
+ mutable BooksByTag myBooksByTag;
+ mutable BookList myRecentBooks;
+
+ mutable std::string myPath;
+ mutable bool myScanSubdirs;
+
+ enum BuildMode {
+ BUILD_NOTHING = 0,
+ BUILD_UPDATE_BOOKS_INFO = 1 << 0,
+ BUILD_COLLECT_FILES_INFO = 1 << 1,
+ BUILD_ALL = 0x03
+ };
+ mutable BuildMode myBuildMode;
+ mutable std::size_t myRevision;
+
+friend class LibrarySynchronizer;
+friend class LibrarySynchronizerWrapper;
+};
+
+#endif /* __LIBRARY_H__ */
diff --git a/fbreader/src/library/Lists.h b/fbreader/src/library/Lists.h
new file mode 100644
index 0000000..9768841
--- /dev/null
+++ b/fbreader/src/library/Lists.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __LISTS_H__
+#define __LISTS_H__
+
+#include <vector>
+#include <set>
+
+#include <shared_ptr.h>
+
+class Book;
+class Author;
+class Tag;
+class BookByFileNameComparator;
+
+typedef std::vector<shared_ptr<Book> > BookList;
+typedef std::set<shared_ptr<Book>,BookByFileNameComparator> BookSet;
+typedef std::vector<shared_ptr<Author> > AuthorList;
+typedef std::vector<shared_ptr<Tag> > TagList;
+typedef std::set<shared_ptr<Tag> > TagSet;
+
+#endif /* __LISTS_H__ */
diff --git a/fbreader/src/library/Number.cpp b/fbreader/src/library/Number.cpp
new file mode 100644
index 0000000..003104e
--- /dev/null
+++ b/fbreader/src/library/Number.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <cstdlib>
+
+#include <ZLStringUtil.h>
+
+#include "Number.h"
+
+static std::string ZERO = "0";
+
+Number::Number() : myNumber(ZERO) {
+}
+
+Number::Number(std::string number) : myNumber(validate(number)) {
+}
+
+Number::Number(int number) {
+ ZLStringUtil::appendNumber(myNumber, number);
+}
+
+std::string Number::validate(const std::string &value) {
+ std::string result = value;
+ while (result.size() > 1 && ZLStringUtil::stringStartsWith(result, ZERO)) {
+ result = result.substr(1);
+ }
+ return result;
+}
+
+const std::string &Number::value() const {
+ return myNumber;
+}
+
+void Number::setValue(const std::string &value) {
+ myNumber = validate(value);
+}
+
+bool Number::operator <(const Number &number) const {
+ int index = std::strtol(myNumber.c_str(), 0, 10);
+ int otherIndex = std::strtol(number.value().c_str(), 0, 10);
+ return index < otherIndex;
+}
+
+bool Number::operator ==(const Number &number) const {
+ int index = std::strtol(myNumber.c_str(), 0, 10);
+ int otherIndex = std::strtol(number.value().c_str(), 0, 10);
+ return index == otherIndex;
+}
diff --git a/fbreader/src/library/Number.h b/fbreader/src/library/Number.h
new file mode 100644
index 0000000..858b4be
--- /dev/null
+++ b/fbreader/src/library/Number.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __NUMBER_H__
+#define __NUMBER_H__
+
+#include <string>
+
+class Number {
+public:
+ Number();
+ Number(std::string number);
+ Number(int number);
+
+ static std::string validate(const std::string& value);
+
+ const std::string &value() const;
+ void setValue(const std::string &value);
+
+ //TODO implement validation
+
+ bool operator< (const Number &number) const;
+ bool operator== (const Number &number) const;
+
+private:
+ std::string myNumber;
+};
+
+#endif /* __NUMBER_H__ */
diff --git a/fbreader/src/library/Tag.cpp b/fbreader/src/library/Tag.cpp
new file mode 100644
index 0000000..c76db47
--- /dev/null
+++ b/fbreader/src/library/Tag.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <set>
+#include <algorithm>
+
+#include <ZLUnicodeUtil.h>
+
+#include "Tag.h"
+
+TagList Tag::ourRootTags;
+std::map <int,shared_ptr<Tag> > Tag::ourTagsById;
+
+const std::string Tag::DELIMITER = "/";
+
+shared_ptr<Tag> Tag::getTag(const std::string &name, shared_ptr<Tag> parent, int tagId) {
+ if (name.empty()) {
+ return 0;
+ }
+ TagList &tags = parent.isNull() ? ourRootTags : parent->myChildren;
+ for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) {
+ if ((*it)->name() == name) {
+ return *it;
+ }
+ }
+ shared_ptr<Tag> t = new Tag(name, parent, tagId);
+ tags.push_back(t);
+ if (tagId > 0) {
+ ourTagsById[tagId] = t;
+ }
+ return t;
+}
+
+shared_ptr<Tag> Tag::getTagByFullName(const std::string &fullName) {
+ std::string tag = fullName;
+ ZLUnicodeUtil::utf8Trim(tag);
+ std::size_t index = tag.rfind(DELIMITER);
+ if (index == std::string::npos) {
+ return getTag(tag);
+ } else {
+ std::string lastName = tag.substr(index + 1);
+ ZLUnicodeUtil::utf8Trim(lastName);
+ return getTag(lastName, getTagByFullName(tag.substr(0, index)));
+ }
+}
+
+shared_ptr<Tag> Tag::getTagById(int tagId) {
+ std::map<int,shared_ptr<Tag> >::const_iterator it = ourTagsById.find(tagId);
+ return it != ourTagsById.end() ? it->second : 0;
+}
+
+shared_ptr<Tag> Tag::cloneSubTag(shared_ptr<Tag> tag, shared_ptr<Tag> oldparent, shared_ptr<Tag> newparent) {
+ std::vector<std::string> levels;
+
+ while (tag != oldparent) {
+ levels.push_back(tag->name());
+ tag = tag->parent();
+ if (tag.isNull()) {
+ return 0;
+ }
+ }
+
+ if (levels.empty()) {
+ return 0;
+ }
+
+ shared_ptr<Tag> res = newparent;
+ while (!levels.empty()) {
+ res = getTag(levels.back(), res);
+ levels.pop_back();
+ }
+ return res;
+}
+
+void Tag::collectAncestors(shared_ptr<Tag> tag, TagList &parents) {
+ for (; !tag.isNull(); tag = tag->parent()) {
+ parents.push_back(tag);
+ }
+ std::reverse(parents.begin(), parents.end());
+}
+
+void Tag::collectTagNames(std::vector<std::string> &tags) {
+ std::set<std::string> tagsSet;
+ TagList stack(ourRootTags);
+ while (!stack.empty()) {
+ shared_ptr<Tag> tag = stack.back();
+ stack.pop_back();
+ tagsSet.insert(tag->fullName());
+ stack.insert(stack.end(), tag->myChildren.begin(), tag->myChildren.end());
+ }
+ tags.insert(tags.end(), tagsSet.begin(), tagsSet.end());
+}
+
+Tag::Tag(const std::string &name, shared_ptr<Tag> parent, int tagId) : myName(name), myParent(parent), myLevel(parent.isNull() ? 0 : parent->level() + 1), myTagId(tagId) {
+}
+
+const std::string &Tag::fullName() const {
+ if (myParent.isNull()) {
+ return myName;
+ }
+ if (myFullName.empty()) {
+ myFullName = myParent->fullName() + DELIMITER + myName;
+ }
+ return myFullName;
+}
+
+bool Tag::isAncestorOf(shared_ptr<Tag> tag) const {
+ if (tag->level() <= level()) {
+ return false;
+ }
+ while (tag->level() > level()) {
+ tag = tag->parent();
+ }
+ return &*tag == this;
+}
+
+void Tag::setTagId(shared_ptr<Tag> tag, int tagId) {
+ if (tag.isNull() || tag->myTagId != 0) {
+ return;
+ }
+ tag->myTagId = tagId;
+ ourTagsById[tagId] = tag;
+}
diff --git a/fbreader/src/library/Tag.h b/fbreader/src/library/Tag.h
new file mode 100644
index 0000000..0bd1f79
--- /dev/null
+++ b/fbreader/src/library/Tag.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __TAG_H__
+#define __TAG_H__
+
+#include <string>
+#include <map>
+
+#include <shared_ptr.h>
+
+#include "Lists.h"
+
+class Tag {
+
+private:
+ static TagList ourRootTags;
+ static std::map<int,shared_ptr<Tag> > ourTagsById;
+
+public:
+ static shared_ptr<Tag> getTag(const std::string &name, shared_ptr<Tag> parent = 0, int tagId = 0);
+ static shared_ptr<Tag> getTagByFullName(const std::string &fullName);
+ static shared_ptr<Tag> getTagById(int tagId);
+
+ static void setTagId(shared_ptr<Tag>, int tagId);
+
+ static shared_ptr<Tag> cloneSubTag(shared_ptr<Tag> tag, shared_ptr<Tag> oldparent, shared_ptr<Tag> newparent);
+
+ static void collectAncestors(shared_ptr<Tag> tag, TagList &parents);
+
+ static void collectTagNames(std::vector<std::string> &tags);
+
+private:
+ static const std::string DELIMITER;
+
+private:
+ Tag(const std::string &name, shared_ptr<Tag> parent, int tagId);
+
+public:
+ const std::string &fullName() const;
+ const std::string &name() const;
+
+ shared_ptr<Tag> parent() const;
+
+public:
+ bool isAncestorOf(shared_ptr<Tag> tag) const;
+
+ int tagId() const;
+ std::size_t level() const;
+
+private:
+ const std::string myName;
+ mutable std::string myFullName;
+
+ shared_ptr<Tag> myParent;
+ TagList myChildren;
+ const std::size_t myLevel;
+
+ int myTagId;
+
+private: // disable copying
+ Tag(const Tag &);
+ const Tag &operator = (const Tag &);
+};
+
+class TagComparator {
+
+public:
+ bool operator () (
+ shared_ptr<Tag> tag0,
+ shared_ptr<Tag> tag1
+ ) const;
+};
+
+inline const std::string &Tag::name() const { return myName; }
+
+inline shared_ptr<Tag> Tag::parent() const { return myParent; }
+
+inline int Tag::tagId() const { return myTagId; }
+inline std::size_t Tag::level() const { return myLevel; }
+
+#endif /* __TAG_H__ */