diff options
author | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-05-11 21:28:48 +0900 |
---|---|---|
committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-05-11 21:28:48 +0900 |
commit | 2462d03f322261bd616721c2b2065c4004b36c9c (patch) | |
tree | 239947a0737bb8386703a1497f12c09aebd3080a /fbreader/src/library | |
download | tde-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.cpp | 67 | ||||
-rw-r--r-- | fbreader/src/library/Author.h | 71 | ||||
-rw-r--r-- | fbreader/src/library/Book.cpp | 280 | ||||
-rw-r--r-- | fbreader/src/library/Book.h | 142 | ||||
-rw-r--r-- | fbreader/src/library/Comparators.cpp | 106 | ||||
-rw-r--r-- | fbreader/src/library/Library.cpp | 439 | ||||
-rw-r--r-- | fbreader/src/library/Library.h | 126 | ||||
-rw-r--r-- | fbreader/src/library/Lists.h | 39 | ||||
-rw-r--r-- | fbreader/src/library/Number.cpp | 64 | ||||
-rw-r--r-- | fbreader/src/library/Number.h | 45 | ||||
-rw-r--r-- | fbreader/src/library/Tag.cpp | 139 | ||||
-rw-r--r-- | fbreader/src/library/Tag.h | 98 |
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 ¤t = (*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__ */ |