diff options
Diffstat (limited to 'fbreader/src/migration/Migration_0_11_0.cpp')
-rw-r--r-- | fbreader/src/migration/Migration_0_11_0.cpp | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/fbreader/src/migration/Migration_0_11_0.cpp b/fbreader/src/migration/Migration_0_11_0.cpp new file mode 100644 index 0000000..144245e --- /dev/null +++ b/fbreader/src/migration/Migration_0_11_0.cpp @@ -0,0 +1,544 @@ +/* + * 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 <iostream> +#include <iomanip> +#include <ZLTime.h> + +#include <vector> +#include <algorithm> + +#include <ZLFile.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> + +#include "BookInfo.h" +#include "Migration.h" +#include "../options/FBCategoryKey.h" + +#include "../formats/FormatPlugin.h" + +#include "../database/booksdb/BooksDBUtil.h" +#include "../database/booksdb/BooksDB.h" +#include "../library/Book.h" +#include "../library/Tag.h" + +static const std::string BOOK_LIST_GROUP = "BookList"; +static const std::string BOOK_LIST_SIZE = "Size"; +static const std::string BOOK_LIST_PREFIX = "Book"; + +static const std::string CURRENT_STATE_GROUP = "State"; + +static const std::string RECENT_BOOKS_GROUP = "LastOpenedBooks"; +static const std::string BOOK = "Book"; +static const std::size_t MaxXmlListSize = 10; + +static const std::string PARAGRAPH_OPTION_NAME = "Paragraph"; +static const std::string WORD_OPTION_NAME = "Word"; +static const std::string CHAR_OPTION_NAME = "Char"; +static const std::string POSITION_IN_BUFFER = "PositionInBuffer"; +static const std::string BUFFER_SIZE = "UndoBufferSize"; +static const char * const BUFFER_PARAGRAPH_PREFIX = "Paragraph_"; +static const char * const BUFFER_WORD_PREFIX = "Word_"; +static const int MaxXmlStackSize = 20; + +static const std::string SIZE = "Size"; +static const std::string ENTRIES_NUMBER = "EntriesNumber"; +static const std::string PALM_TYPE = "PalmType"; + +static const std::string NET_FILES_GROUP = "Files"; + +class Migration_0_11_0_Runnable : public DBRunnable { + +public: + bool run(); + +private: + bool migrateBooks(); + bool migrateBookList(); + bool migrateState(); + bool migrateNetwork(); + + bool migrateBook(const ZLFile &file); + + std::string tags2string(const TagList &tags); + + bool stringEquals(const std::string &tags1, const std::string &tags2); + + bool migrateRecentBooks(); + + bool migrateBooksState(); + + bool migrateBookStateStack(const std::string &fileName, const Book &book); + bool migrateBookLastState(const std::string &fileName, const Book &book); + + bool shouldReadDisk(const std::string &fileName); + + bool clearBooksOptions(); + + static void moveOption(const ZLCategoryKey &category, const std::string &oldGroup, const std::string &newGroup, const std::string &name, const std::string &defaultValue); + static void moveOption(const ZLCategoryKey &category, const std::string &oldGroup, const std::string &newGroup, const std::string &name, int defaultValue); + static void movePercentGroups(std::vector<std::string> &optionGroups); + static void moveBookGroup(const std::string &oldgroup, const std::string &newgroup); + +private: + std::map<std::string, shared_ptr<Book> > myBooks; +}; + + +inline bool Migration_0_11_0_Runnable::shouldReadDisk(const std::string &fileName) { + const std::string ext = ZLFile(fileName).extension(); + return ext == "fb2" || ext == "epub" || ext == "mobi" || ext == "oebzip" || ext == "opf"; + //return ext == "fb2"; + //return true; +} + + +void Migration_0_11_0_Runnable::moveOption(const ZLCategoryKey &category, const std::string &oldGroup, const std::string &newGroup, const std::string &name, const std::string &defaultValue) { + ZLStringOption newOption(category, newGroup, name, defaultValue); + ZLStringOption oldOption(category, oldGroup, name, defaultValue); + newOption.setValue(oldOption.value()); + oldOption.setValue(defaultValue); +} + +void Migration_0_11_0_Runnable::moveOption(const ZLCategoryKey &category, const std::string &oldGroup, const std::string &newGroup, const std::string &name, int defaultValue) { + ZLIntegerOption newOption(category, newGroup, name, defaultValue); + ZLIntegerOption oldOption(category, oldGroup, name, defaultValue); + newOption.setValue(oldOption.value()); + oldOption.setValue(defaultValue); +} + +static bool percentPathPredicate(const std::string &path) { + static const std::string _start = "%APPLICATION_PATH%"; + return ZLStringUtil::stringStartsWith(path, _start); +} + +void Migration_0_11_0_Runnable::movePercentGroups(std::vector<std::string> &optionGroups) { + std::vector<std::string>::iterator it = optionGroups.begin(); + while ((it = std::find_if(it, optionGroups.end(), percentPathPredicate)) != optionGroups.end()) { + const std::string oldgroup = *it; + const std::string newgroup = ZLFile(oldgroup).resolvedPath(); + if (std::find(optionGroups.begin(), optionGroups.end(), newgroup) == optionGroups.end()) { + moveBookGroup(oldgroup, newgroup); + *it++ = newgroup; + } else { + if (BookInfo(newgroup).TitleOption.value().empty()) { + moveBookGroup(oldgroup, newgroup); + } + it = optionGroups.erase(it); + } + ZLOption::clearGroup(oldgroup); + } +} + +void Migration_0_11_0_Runnable::moveBookGroup(const std::string &oldgroup, const std::string &newgroup) { + BookInfo oldbi(oldgroup); + BookInfo newbi(newgroup); + newbi = oldbi; + oldbi.reset(); + + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, PARAGRAPH_OPTION_NAME, 0); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, WORD_OPTION_NAME, 0); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, CHAR_OPTION_NAME, 0); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, POSITION_IN_BUFFER, 0); + + int stackSize = ZLIntegerOption(ZLCategoryKey::STATE, oldgroup, BUFFER_SIZE, 0).value(); + for (int i = 0; i < stackSize; ++i) { + std::string bufferParagraph = BUFFER_PARAGRAPH_PREFIX; + std::string bufferWord = BUFFER_WORD_PREFIX; + ZLStringUtil::appendNumber(bufferParagraph, i); + ZLStringUtil::appendNumber(bufferWord, i); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, bufferParagraph, -1); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, bufferWord, -1); + } + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, BUFFER_SIZE, 0); +} + + + +Migration_0_11_0::Migration_0_11_0() : Migration("0.11.0") { +} + +void Migration_0_11_0::doMigrationInternal() { + Migration_0_11_0_Runnable r; + BooksDB::Instance().executeAsTransaction(r); + //r.run(); +} + +bool Migration_0_11_0_Runnable::run() { +const ZLTime start; + if (!migrateBooks()) { + std::cerr << std::endl << "VERDICT: migrateBooks failed" << std::endl << std::endl; + } +std::cerr << "migration total 0: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + if (!migrateBookList()) { + std::cerr << std::endl << "VERDICT: migrateBookList failed" << std::endl << std::endl; + } +std::cerr << "migration total 1: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + if (!migrateState()) { + std::cerr << std::endl << "VERDICT: migrateState failed" << std::endl << std::endl; + } +std::cerr << "migration total 2: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + if (!migrateNetwork()) { + std::cerr << std::endl << "VERDICT: migrateNetwork failed" << std::endl << std::endl; + } +std::cerr << "migration total 3: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + if (!clearBooksOptions()) { + std::cerr << std::endl << "VERDICT: clearBooksOptions failed" << std::endl << std::endl; + } +std::cerr << "migration total 4: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + return true; +} + + + +bool Migration_0_11_0_Runnable::migrateBooks() { + /*std::map<std::string, unsigned long> ext2time; + std::map<std::string, unsigned long> ext2num; + unsigned long totalTime = 0, totalNum = 0;*/ + + PluginCollection &collection = PluginCollection::Instance(); + std::vector<std::string> optionGroups; + ZLOption::listOptionGroups(optionGroups); + + movePercentGroups(optionGroups); + + bool res = true; + for (std::vector<std::string>::const_iterator it = optionGroups.begin(); it != optionGroups.end(); ++it) { + const std::string &name = *it; + if (Migration::isLikeToFileName(name)) { + /* TODO: check correctness of migration order: + * 1) palmType + * 2) size + * 3) book (depends on palmType and size) + */ + const std::string palmType = ZLStringOption(FBCategoryKey::BOOKS, name, PALM_TYPE, "").value(); + if (!palmType.empty()) { + BooksDB::Instance().setPalmType(name, palmType); + } + ZLStringOption(FBCategoryKey::BOOKS, name, PALM_TYPE, "").setValue(""); // clean books.xml + ZLFile file(name); + if (file.physicalFilePath() == name) { + int size = ZLIntegerOption(FBCategoryKey::BOOKS, name, SIZE, -1).value(); + if (size != -1) { + BooksDB::Instance().setFileSize(name, size); + } + } + if (collection.plugin(file, false) != 0) { + if (!BookInfo(name).TitleOption.value().empty()) { + //ZLTime start; + if (!migrateBook(ZLFile(name))) { + std::cerr << "ERROR(2): migrateBook failed" << std::endl; + res = false; + } + /*ZLTime end; + { + unsigned time = end.millisecondsFrom(start); + std::string ext = ZLFile(name).extension(); + totalTime += time; + totalNum += 1; + ext2time[ext] += time; + ext2num[ext] += 1; + }*/ + } + BookInfo(name).reset(); // clean books.xml + } else { + ZLOption::clearGroup(name); // clean books.xml + } + ZLIntegerOption(FBCategoryKey::BOOKS, name, SIZE, -1).setValue(-1); // clean books.xml + if (!ZLStringOption(FBCategoryKey::BOOKS, name, ENTRIES_NUMBER, "").value().empty()) { + ZLOption::clearGroup(name); // clean books.xml + } + } + } + + /*std::cerr << " ext" << " time,ms" << " time/total,%" << " number of books" << " time for 1 book,ms" << std::endl; + std::cerr << "---------------------------------------------------------------------------" << std::endl; + for (std::map<std::string, unsigned long>::const_iterator it = ext2time.begin(); it != ext2time.end(); ++it) { + const std::string &ext = it->first; + unsigned long time = it->second; + unsigned long num = ext2num[ext]; + std::cerr << std::setw(8) << ext << std::setw(10) << time << std::setw(15) << ((float) time) / totalTime * 100.0 + << std::setw(18) << num << std::setw(22) << ((float) time) / num << std::endl; + } + std::cerr << "---------------------------------------------------------------------------" << std::endl; + std::cerr << "total:" << std::endl; + std::cerr << std::setw(8) << "" << std::setw(10) << totalTime << std::setw(15) << "" + << std::setw(20) << totalNum << std::setw(20) << "" << std::endl;*/ + + return res; +} + + +bool Migration_0_11_0_Runnable::migrateBook(const ZLFile &file) { + shared_ptr<Book> infoBook = Book::loadFromBookInfo(file); + if (infoBook.isNull()) { + std::cerr << "ERROR: loading book from BookInfo failed: " << file.path() << std::endl; + return false; + } + if (shouldReadDisk(file.path()) && BooksDBUtil::isBookFull(*infoBook)) { + shared_ptr<Book> fileBook = Book::loadFromFile(file); + //shared_ptr<Book> fileBook = infoBook; + //shared_ptr<Book> fileBook; + if (!fileBook.isNull()) { + std::string tagList1 = tags2string(infoBook->tags()); + std::string tagList2 = tags2string(fileBook->tags()); + if (stringEquals(tagList1, tagList2)) { + infoBook->removeAllTags(); + const TagList &tList = fileBook->tags(); + for (TagList::const_iterator it = tList.begin(); it != tList.end(); ++it) { + infoBook->addTag(*it); + } + } + } + } + myBooks.insert(std::make_pair(file.path(), infoBook)); + const bool code = BooksDB::Instance().saveBook(infoBook); + if (!code) { + std::cerr << "ERROR: saving book to database failed: " << file.path() << std::endl; + } + return code; +} + +std::string Migration_0_11_0_Runnable::tags2string(const TagList &tags) { + std::string tagList; + TagList::const_iterator it = tags.begin(); + if (it != tags.end()) { + tagList += (*it++)->fullName(); + while (it != tags.end()) { + tagList += ','; + tagList += (*it++)->fullName(); + } + } + return tagList; +} + +bool Migration_0_11_0_Runnable::stringEquals(const std::string &tags1, const std::string &tags2) { + std::size_t i1 = 0; + std::size_t i2 = 0; + while (i1 < tags1.size() && i2 < tags2.size()) { + if (std::isspace(tags1[i1])) { + ++i1; + continue; + } + if (std::isspace(tags2[i2])) { + ++i2; + continue; + } + if (tags1[i1++] != tags2[i2++]) { + return false; + } + } + if (i1 == tags1.size() && i2 < tags2.size()) { + while (i2 < tags2.size()) { + if (!std::isspace(tags2[i2++])) { + return false; + } + } + return true; + } + if (i1 < tags1.size() && i2 == tags2.size()) { + while (i1 < tags1.size()) { + if (!std::isspace(tags1[i1++])) { + return false; + } + } + return true; + } + return true; +} + +bool Migration_0_11_0_Runnable::migrateBookList() { + bool res = true; + int size = ZLIntegerOption(ZLCategoryKey::STATE, BOOK_LIST_GROUP, BOOK_LIST_SIZE, 0).value(); + for (int i = 0; i < size; ++i) { + std::string optionName = BOOK_LIST_PREFIX; + ZLStringUtil::appendNumber(optionName, i); + const std::string &fileName = ZLStringOption(ZLCategoryKey::STATE, BOOK_LIST_GROUP, optionName, "").value(); + if (!fileName.empty()) { + std::map<std::string, shared_ptr<Book> >::iterator it = myBooks.find(fileName); + if (it != myBooks.end()) { + shared_ptr<Book> book = it->second; + if (!book.isNull() && book->bookId() != 0) { + if (!BooksDB::Instance().insertIntoBookList(*book)) { + std::cerr << "ERROR: insert into BookList failed: " << fileName << std::endl; + res = false; + } + } + } + } + } + ZLOption::clearGroup(BOOK_LIST_GROUP); // clean state.xml + return res; +} + +bool Migration_0_11_0_Runnable::migrateState() { + bool res = true; + if (!migrateRecentBooks()) { + std::cerr << "ERROR(2): migrateRecentBooks failed" << std::endl; + res = false; + } + if (!migrateBooksState()) { + std::cerr << "ERROR(2): migrateBooksState failed" << std::endl; + res = false; + } + ZLOption::clearGroup(RECENT_BOOKS_GROUP); // clean state.xml + ZLOption::clearGroup(CURRENT_STATE_GROUP); // clean state.xml + return res; +} + +bool Migration_0_11_0_Runnable::migrateRecentBooks() { + BookList books; + for (std::size_t i = 0; i < MaxXmlListSize; ++i) { + std::string num = BOOK; + ZLStringUtil::appendNumber(num, i); + std::string name = ZLStringOption(ZLCategoryKey::STATE, RECENT_BOOKS_GROUP, num, "").value(); + if (!name.empty()) { + //shared_ptr<Book> book = BooksDBUtil::getBook(name, false); + std::map<std::string, shared_ptr<Book> >::const_iterator it = myBooks.find(name); + if (it == myBooks.end()) { + if ((it = myBooks.find(ZLFile(name).resolvedPath())) == myBooks.end()) { + continue; + } + } + shared_ptr<Book> book = it->second; + if (!book.isNull() && book->bookId() != 0 && std::find(books.begin(), books.end(), book) == books.end()) { + books.push_back(book); + } + } + } + bool res = BooksDB::Instance().saveRecentBooks(books); + if (!res) { + std::cerr << "ERROR: saving recent books list failed (" << books.size() << " item[s])" << std::endl; + } + return res; +} + +bool Migration_0_11_0_Runnable::migrateBooksState() { + bool res = true; + + for (std::map<std::string, shared_ptr<Book> >::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + const std::string &fileName = it->first; + if (it->second.isNull()) { + std::cerr << "ERROR: book in map is null: " << fileName << std::endl; + res = false; + continue; + } + const Book &book = *it->second; + if (!migrateBookStateStack(fileName, book)) { + res = false; + } + if (!migrateBookLastState(fileName, book)) { + res = false; + } + } + return res; +} + + +bool Migration_0_11_0_Runnable::migrateBookStateStack(const std::string &fileName, const Book &book) { + std::deque<ReadingState> stack; + bool res = true; + int stackSize = ZLIntegerOption(ZLCategoryKey::STATE, fileName, BUFFER_SIZE, 0).value(); + if (stackSize > 0) { + if (stackSize > MaxXmlStackSize) { + stackSize = MaxXmlStackSize; + } + for (int i = 0; i < stackSize; ++i) { + std::string bufferParagraph = BUFFER_PARAGRAPH_PREFIX; + std::string bufferWord = BUFFER_WORD_PREFIX; + ZLStringUtil::appendNumber(bufferParagraph, i); + ZLStringUtil::appendNumber(bufferWord, i); + ReadingState pos( + ZLIntegerOption(ZLCategoryKey::STATE, fileName, bufferParagraph, -1).value(), + ZLIntegerOption(ZLCategoryKey::STATE, fileName, bufferWord, -1).value(), + 0 + ); + stack.push_back(pos); + ZLIntegerOption(ZLCategoryKey::STATE, fileName, bufferParagraph, -1).setValue(-1); // clean state.xml + ZLIntegerOption(ZLCategoryKey::STATE, fileName, bufferWord, -1).setValue(-1); // clean state.xml + } + if (!BooksDB::Instance().saveBookStateStack(book, stack)) { + std::cerr << "ERROR: saving book state stack failed: " << fileName << std::endl; + res = false; + } + stack.clear(); + } + ZLIntegerOption(ZLCategoryKey::STATE, fileName, BUFFER_SIZE, 0).setValue(0); // clean state.xml + return res; +} + +bool Migration_0_11_0_Runnable::migrateBookLastState(const std::string &fileName, const Book &book) { + const ReadingState state( + ZLIntegerOption(ZLCategoryKey::STATE, fileName, PARAGRAPH_OPTION_NAME, 0).value(), + ZLIntegerOption(ZLCategoryKey::STATE, fileName, WORD_OPTION_NAME, 0).value(), + ZLIntegerOption(ZLCategoryKey::STATE, fileName, CHAR_OPTION_NAME, 0).value() + ); + const int stackPos = ZLIntegerOption(ZLCategoryKey::STATE, fileName, POSITION_IN_BUFFER, 0).value(); + if (state.Paragraph == 0 && state.Word == 0 && state.Character == 0 && stackPos == 0) { + return true; + } + ZLIntegerOption(ZLCategoryKey::STATE, fileName, PARAGRAPH_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, fileName, WORD_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, fileName, CHAR_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, fileName, POSITION_IN_BUFFER, 0).setValue(0); + bool res1 = BooksDB::Instance().setBookState(book, state); + bool res2 = BooksDB::Instance().setStackPos(book, stackPos); + if (!res1) { + std::cerr << "ERROR: saving book last state failed: " << fileName << std::endl; + } + if (!res2) { + std::cerr << "ERROR: saving book state stack position failed: " << fileName << std::endl; + } + return res1 && res2; +} + +bool Migration_0_11_0_Runnable::migrateNetwork() { + bool res = true; +// FBReader desktop 0.99.1 deprecates NetFiles table, so don't fill it +// std::vector<std::string> urls; +// ZLOption::listOptionNames(NET_FILES_GROUP, urls); +// for (std::vector<std::string>::const_iterator it = urls.begin(); it != urls.end(); ++it) { +// const std::string &url = *it; +// const std::string fileName = ZLStringOption(ZLCategoryKey::NETWORK, NET_FILES_GROUP, url, "").value(); +// if (!BooksDB::Instance().setNetFile(url, fileName)) { +// std::cerr << "ERROR: saving file's URL failed: " << std::endl +// << "\tURL = " << url << std::endl +// << "\tfileName = " << fileName << std::endl; +// res = false; +// } +// } + ZLOption::clearGroup(NET_FILES_GROUP); // clean state.xml + return res; +} + +bool Migration_0_11_0_Runnable::clearBooksOptions() { + bool res = true; + for (std::map<std::string, shared_ptr<Book> >::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + const std::string &fileName = it->first; + if (it->second.isNull()) { + std::cerr << "ERROR: book in map is null in clearBooksOptions: " << fileName << std::endl; + res = false; + continue; + } + ZLOption::clearGroup(fileName); // clear books.xml & state.xml + } + return res; +} + |