summaryrefslogtreecommitdiffstats
path: root/fbreader/src/database/booksdb
diff options
context:
space:
mode:
Diffstat (limited to 'fbreader/src/database/booksdb')
-rw-r--r--fbreader/src/database/booksdb/BooksDB.cpp503
-rw-r--r--fbreader/src/database/booksdb/BooksDB.h174
-rw-r--r--fbreader/src/database/booksdb/BooksDBQuery.cpp327
-rw-r--r--fbreader/src/database/booksdb/BooksDBQuery.h99
-rw-r--r--fbreader/src/database/booksdb/BooksDBUtil.cpp184
-rw-r--r--fbreader/src/database/booksdb/BooksDBUtil.h61
-rw-r--r--fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp76
-rw-r--r--fbreader/src/database/booksdb/BooksDB_BookSeries.cpp83
-rw-r--r--fbreader/src/database/booksdb/BooksDB_BookTag.cpp246
-rw-r--r--fbreader/src/database/booksdb/DBRunnables.h311
-rw-r--r--fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp46
-rw-r--r--fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp37
-rw-r--r--fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp60
-rw-r--r--fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp123
-rw-r--r--fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp40
-rw-r--r--fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp68
-rw-r--r--fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp51
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp75
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp43
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp56
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp49
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp58
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp80
23 files changed, 2850 insertions, 0 deletions
diff --git a/fbreader/src/database/booksdb/BooksDB.cpp b/fbreader/src/database/booksdb/BooksDB.cpp
new file mode 100644
index 0000000..bf6d2b3
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDB.cpp
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 <ZLibrary.h>
+#include <ZLFile.h>
+#include <ZLDir.h>
+#include <ZLLanguageUtil.h>
+
+#include "BooksDB.h"
+#include "BooksDBQuery.h"
+
+#include "../../library/Book.h"
+#include "../../library/Author.h"
+#include "../../library/Tag.h"
+
+#include "../sqldb/implsqlite/SQLiteFactory.h"
+
+shared_ptr<BooksDB> BooksDB::ourInstance = 0;
+
+const std::string BooksDB::DATABASE_NAME = "books.db";
+const std::string BooksDB::STATE_DATABASE_NAME = "state.db";
+
+BooksDB &BooksDB::Instance() {
+ if (ourInstance.isNull()) {
+ ZLFile dir(databaseDirName());
+ dir.directory(true);
+ ZLFile file(databaseDirName() + ZLibrary::FileNameDelimiter + DATABASE_NAME);
+ ourInstance = new BooksDB(file.physicalFilePath());
+ ourInstance->initDatabase();
+ }
+ return *ourInstance;
+}
+
+BooksDB::BooksDB(const std::string &path) : SQLiteDataBase(path), myInitialized(false) {
+ initCommands();
+}
+
+BooksDB::~BooksDB() {
+}
+
+bool BooksDB::initDatabase() {
+ if (isInitialized()) {
+ return true;
+ }
+
+ if (!open()) {
+ return false;
+ }
+
+ myInitialized = true;
+
+ ZLFile stateFile(databaseDirName() + ZLibrary::FileNameDelimiter + STATE_DATABASE_NAME);
+ shared_ptr<DBCommand> cmd = SQLiteFactory::createCommand(BooksDBQuery::PREINIT_DATABASE, connection(), "@stateFile", DBValue::DBTEXT);
+ ((DBTextValue&)*cmd->parameter("@stateFile").value()) = stateFile.physicalFilePath();
+ if (!cmd->execute()) {
+ myInitialized = false;
+ close();
+ return false;
+ }
+
+ shared_ptr<DBRunnable> runnable = new InitBooksDBRunnable(connection());
+ if (!executeAsTransaction(*runnable)) {
+ myInitialized = false;
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+void BooksDB::initCommands() {
+ myLoadBook = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK, connection(), "@file_id", DBValue::DBINT);
+ myGetFileSize = SQLiteFactory::createCommand(BooksDBQuery::GET_FILE_SIZE, connection(), "@file_id", DBValue::DBINT);
+ mySetFileSize = SQLiteFactory::createCommand(BooksDBQuery::SET_FILE_SIZE, connection(), "@file_id", DBValue::DBINT, "@size", DBValue::DBINT);
+ myFindFileName = SQLiteFactory::createCommand(BooksDBQuery::FIND_FILE_NAME, connection(), "@file_id", DBValue::DBINT);
+ myFindAuthorId = SQLiteFactory::createCommand(BooksDBQuery::FIND_AUTHOR_ID, connection(), "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT);
+
+ myLoadBooks = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOKS, connection());
+
+ myLoadBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK_STATE_STACK, connection(), "@book_id", DBValue::DBINT);
+
+ myGetPalmType = SQLiteFactory::createCommand(BooksDBQuery::GET_PALM_TYPE, connection(), "@file_id", DBValue::DBINT);
+ mySetPalmType = SQLiteFactory::createCommand(BooksDBQuery::SET_PALM_TYPE, connection(), "@file_id", DBValue::DBINT, "@type", DBValue::DBTEXT);
+
+ myLoadStackPos = SQLiteFactory::createCommand(BooksDBQuery::LOAD_STACK_POS, connection(), "@book_id", DBValue::DBINT);
+ mySetStackPos = SQLiteFactory::createCommand(BooksDBQuery::SET_STACK_POS, connection(), "@book_id", DBValue::DBINT, "@stack_pos", DBValue::DBINT);
+
+ myLoadBookState = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK_STATE, connection(), "@book_id", DBValue::DBINT);
+ mySetBookState = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_STATE, connection(), "@book_id", DBValue::DBINT, "@paragraph", DBValue::DBINT, "@word", DBValue::DBINT, "@char", DBValue::DBINT);
+
+ myInsertBookList = SQLiteFactory::createCommand(BooksDBQuery::INSERT_BOOK_LIST, connection(), "@book_id", DBValue::DBINT);
+ myDeleteBookList = SQLiteFactory::createCommand(BooksDBQuery::DELETE_BOOK_LIST, connection(), "@book_id", DBValue::DBINT);
+ myCheckBookList = SQLiteFactory::createCommand(BooksDBQuery::CHECK_BOOK_LIST, connection(), "@book_id", DBValue::DBINT);
+
+ mySaveTableBook = new SaveTableBookRunnable(connection());
+ mySaveAuthors = new SaveAuthorsRunnable(connection());
+ mySaveSeries = new SaveSeriesRunnable(connection());
+ mySaveTags = new SaveTagsRunnable(connection());
+ mySaveBook = new SaveBookRunnable(*mySaveTableBook, *mySaveAuthors, *mySaveSeries, *mySaveTags);
+ mySaveFileEntries = new SaveFileEntriesRunnable(connection());
+
+ myFindFileId = new FindFileIdRunnable(connection());
+
+ myLoadFileEntries = new LoadFileEntriesRunnable(connection());
+
+ myLoadRecentBooks = new LoadRecentBooksRunnable(connection());
+ mySaveRecentBooks = new SaveRecentBooksRunnable(connection());
+
+ mySaveBookStateStack = new SaveBookStateStackRunnable(connection());
+
+ myDeleteBook = new DeleteBookRunnable(connection());
+}
+
+bool BooksDB::clearDatabase() {
+ if (!isInitialized()) {
+ return false;
+ }
+ shared_ptr<DBRunnable> runnable = new ClearBooksDBRunnable(connection());
+ return executeAsTransaction(*runnable);
+}
+
+shared_ptr<Book> BooksDB::loadBook(const std::string &fileName) {
+ if (!isInitialized()) {
+ return 0;
+ }
+
+ myFindFileId->setFileName(fileName);
+ if (!myFindFileId->run()) {
+ return 0;
+ }
+ ((DBIntValue&)*myLoadBook->parameter("@file_id").value()) = myFindFileId->fileId();
+ shared_ptr<DBDataReader> reader = myLoadBook->executeReader();
+
+ if (reader.isNull() || !reader->next() ||
+ reader->type(0) != DBValue::DBINT /* book_id */) {
+ return 0;
+ }
+ const int bookId = reader->intValue(0);
+
+ shared_ptr<Book> book = Book::createBook(
+ ZLFile(fileName), bookId,
+ reader->textValue(1, Book::AutoEncoding),
+ reader->textValue(2, ZLLanguageUtil::OtherLanguageCode),
+ reader->textValue(3, std::string())
+ );
+
+ loadSeries(*book);
+ loadAuthors(*book);
+ loadTags(*book);
+
+ return book;
+}
+
+
+bool BooksDB::saveBook(const shared_ptr<Book> book) {
+ if (!isInitialized()) {
+ return false;
+ }
+ mySaveBook->setBook(book);
+ return executeAsTransaction(*mySaveBook);
+}
+
+bool BooksDB::saveAuthors(const shared_ptr<Book> book) {
+ if (!isInitialized()) {
+ return false;
+ }
+ mySaveAuthors->setBook(book);
+ return executeAsTransaction(*mySaveAuthors);
+}
+
+bool BooksDB::saveSeries(const shared_ptr<Book> book) {
+ if (!isInitialized()) {
+ return false;
+ }
+ mySaveSeries->setBook(book);
+ return executeAsTransaction(*mySaveSeries);
+}
+
+bool BooksDB::saveTags(const shared_ptr<Book> book) {
+ if (!isInitialized()) {
+ return false;
+ }
+ mySaveTags->setBook(book);
+ return executeAsTransaction(*mySaveTags);
+}
+
+int BooksDB::getFileSize(const std::string fileName) {
+ if (!isInitialized()) {
+ return -1;
+ }
+ myFindFileId->setFileName(fileName);
+ if (!myFindFileId->run()) {
+ return 0;
+ }
+ ((DBIntValue&)*myGetFileSize->parameter("@file_id").value()) = myFindFileId->fileId();
+
+ shared_ptr<DBValue> fileSize = myGetFileSize->executeScalar();
+
+ if (fileSize.isNull()) {
+ return -1;
+ }
+ if (fileSize->type() == DBValue::DBNULL) {
+ return 0;
+ }
+ if (fileSize->type() != DBValue::DBINT) {
+ return -1;
+ }
+ return ((DBIntValue&)*fileSize).value();
+}
+
+bool BooksDB::setFileSize(const std::string fileName, int size) {
+ if (!isInitialized()) {
+ return false;
+ }
+ myFindFileId->setFileName(fileName, true);
+ if (!executeAsTransaction(*myFindFileId)) {
+ return false;
+ }
+ ((DBIntValue&)*mySetFileSize->parameter("@file_id").value()) = myFindFileId->fileId();
+ ((DBIntValue&)*mySetFileSize->parameter("@size").value()) = size;
+ return mySetFileSize->execute();
+}
+
+bool BooksDB::setEncoding(const Book &book, const std::string &encoding) {
+ if (!isInitialized()) {
+ return false;
+ }
+
+ shared_ptr<DBCommand> command = SQLiteFactory::createCommand(BooksDBQuery::SET_ENCODING, connection());
+
+ command->parameters().push_back(DBCommandParameter("@book_id", new DBIntValue(book.bookId())));
+ command->parameters().push_back(DBCommandParameter("@encoding", new DBTextValue(encoding)));
+
+ return command->execute();
+}
+
+bool BooksDB::loadFileEntries(const std::string &fileName, std::vector<std::string> &entries) {
+ myLoadFileEntries->setFileName(fileName);
+ if (!myLoadFileEntries->run()) {
+ return false;
+ }
+ myLoadFileEntries->collectEntries(entries);
+ return true;
+}
+
+bool BooksDB::saveFileEntries(const std::string &fileName, const std::vector<std::string> &entries) {
+ if (!isInitialized()) {
+ return false;
+ }
+ mySaveFileEntries->setEntries(fileName, entries);
+ return executeAsTransaction(*mySaveFileEntries);
+}
+
+bool BooksDB::loadRecentBooks(std::vector<std::string> &fileNames) {
+ std::vector<int> fileIds;
+ if (!myLoadRecentBooks->run()) {
+ return false;
+ }
+ myLoadRecentBooks->collectFileIds(fileIds);
+ for (std::vector<int>::const_iterator it = fileIds.begin(); it != fileIds.end(); ++it) {
+ const int fileId = *it;
+ const std::string fileName = getFileName(fileId);
+ fileNames.push_back(fileName);
+ }
+ return true;
+}
+
+bool BooksDB::saveRecentBooks(const BookList &books) {
+ if (!isInitialized()) {
+ return false;
+ }
+ mySaveRecentBooks->setBooks(books);
+ return executeAsTransaction(*mySaveRecentBooks);
+}
+
+std::string BooksDB::getFileName(int fileId) {
+ std::string fileName;
+ DBIntValue &findFileId = (DBIntValue&)*myFindFileName->parameter("@file_id").value();
+ findFileId = fileId;
+ while (true) {
+ shared_ptr<DBDataReader> reader = myFindFileName->executeReader();
+ if (reader.isNull() || !reader->next()) {
+ return std::string();
+ }
+ const std::string namePart = reader->textValue(0, std::string());
+ switch (reader->type(1)) { /* parent_id */
+ default:
+ return std::string();
+ case DBValue::DBNULL:
+ return namePart + ZLibrary::FileNameDelimiter + fileName;
+ case DBValue::DBINT:
+ if (fileName.empty()) {
+ fileName = namePart;
+ } else {
+ fileName = namePart + BooksDBQuery::ArchiveEntryDelimiter + fileName;
+ }
+ findFileId = reader->intValue(1);
+ break;
+ }
+ }
+}
+
+bool BooksDB::loadBooks(BookList &books) {
+ shared_ptr<DBDataReader> reader = myLoadBooks->executeReader();
+
+ books.clear();
+ std::map<int,shared_ptr<Book> > bookMap;
+
+ while (reader->next()) {
+ if (reader->type(0) != DBValue::DBINT || /* book_id */
+ reader->type(4) != DBValue::DBINT) { /* file_id */
+ return false;
+ }
+ const int bookId = reader->intValue(0);
+ const int fileId = reader->intValue(4);
+ const std::string fileName = getFileName(fileId);
+
+ shared_ptr<Book> book = Book::createBook(
+ ZLFile(fileName),
+ bookId,
+ reader->textValue(1, Book::AutoEncoding),
+ reader->textValue(2, ZLLanguageUtil::OtherLanguageCode),
+ reader->textValue(3, std::string())
+ );
+ books.push_back(book);
+ bookMap[bookId] = book;
+ }
+
+ loadSeries(bookMap);
+ loadAuthors(bookMap);
+ loadTags(bookMap);
+
+ return true;
+}
+
+bool BooksDB::loadBookStateStack(const Book &book, std::deque<ReadingState> &stack) {
+ if (book.bookId() == 0) {
+ return false;
+ }
+ ((DBIntValue&)*myLoadBookStateStack->parameter("@book_id").value()) = book.bookId();
+ shared_ptr<DBDataReader> reader = myLoadBookStateStack->executeReader();
+ if (reader.isNull()) {
+ return false;
+ }
+ while (reader->next()) {
+ if (reader->type(0) != DBValue::DBINT /* paragraph */
+ || reader->type(1) != DBValue::DBINT /* word */
+ || reader->type(2) != DBValue::DBINT /* char */) {
+ return false;
+ }
+ const int paragraph = reader->intValue(0);
+ const int word = reader->intValue(1);
+ const int character = reader->intValue(2);
+ stack.push_back(ReadingState(paragraph, word, character));
+ }
+ return true;
+}
+
+bool BooksDB::saveBookStateStack(const Book &book, const std::deque<ReadingState> &stack) {
+ if (!isInitialized() || book.bookId() == 0) {
+ return false;
+ }
+ mySaveBookStateStack->setState(book.bookId(), stack);
+ return executeAsTransaction(*mySaveBookStateStack);
+}
+
+
+bool BooksDB::removeBook(const Book &book) {
+ if (!isInitialized() || book.bookId() == 0) {
+ return false;
+ }
+ myDeleteBook->setFileName(book.file().path());
+ return executeAsTransaction(*myDeleteBook);
+}
+
+std::string BooksDB::getPalmType(const std::string &fileName) {
+ if (!isInitialized()) {
+ return "";
+ }
+ myFindFileId->setFileName(fileName);
+ if (!myFindFileId->run()) {
+ return "";
+ }
+ ((DBIntValue&)*myGetPalmType->parameter("@file_id").value()) = myFindFileId->fileId();
+ shared_ptr<DBValue> value = myGetPalmType->executeScalar();
+ if (value.isNull() || value->type() != DBValue::DBTEXT) {
+ return "";
+ }
+ return ((DBTextValue&)*value).value();
+}
+
+bool BooksDB::setPalmType(const std::string &fileName, const std::string &type) {
+ if (!isInitialized()) {
+ return false;
+ }
+ myFindFileId->setFileName(fileName, true);
+ if (!myFindFileId->run()) {
+ return "";
+ }
+ ((DBIntValue&)*mySetPalmType->parameter("@file_id").value()) = myFindFileId->fileId();
+ ((DBTextValue&)*mySetPalmType->parameter("@type").value()) = type;
+ return mySetPalmType->execute();
+}
+
+bool BooksDB::loadBookState(const Book &book, ReadingState &state) {
+ state.Paragraph = state.Word = state.Character = 0;
+ if (book.bookId() == 0) {
+ return false;
+ }
+ ((DBIntValue&)*myLoadBookState->parameter("@book_id").value()) = book.bookId();
+ shared_ptr<DBDataReader> reader = myLoadBookState->executeReader();
+ if (reader.isNull()) {
+ return false;
+ }
+ if (!reader->next()
+ || reader->type(0) != DBValue::DBINT /* paragraph */
+ || reader->type(1) != DBValue::DBINT /* word */
+ || reader->type(2) != DBValue::DBINT /* char */) {
+ return false;
+ }
+ state.Paragraph = reader->intValue(0);
+ state.Word = reader->intValue(1);
+ state.Character = reader->intValue(2);
+ return true;
+}
+
+bool BooksDB::setBookState(const Book &book, const ReadingState &state) {
+ if (book.bookId() == 0) {
+ return false;
+ }
+ ((DBIntValue&)*mySetBookState->parameter("@book_id").value()) = book.bookId();
+ ((DBIntValue&)*mySetBookState->parameter("@paragraph").value()) = state.Paragraph;
+ ((DBIntValue&)*mySetBookState->parameter("@word").value()) = state.Word;
+ ((DBIntValue&)*mySetBookState->parameter("@char").value()) = state.Character;
+ return mySetBookState->execute();
+}
+
+int BooksDB::loadStackPos(const Book &book) {
+ if (book.bookId() == 0) {
+ return 0;
+ }
+ ((DBIntValue&)*myLoadStackPos->parameter("@book_id").value()) = book.bookId();
+ shared_ptr<DBValue> stackPosValue = myLoadStackPos->executeScalar();
+ if (stackPosValue.isNull()
+ || stackPosValue->type() != DBValue::DBINT) {
+ return 0;
+ }
+ return ((DBIntValue&)*stackPosValue).value();
+}
+
+bool BooksDB::setStackPos(const Book &book, int stackPos) {
+ if (book.bookId() == 0) {
+ return false;
+ }
+ ((DBIntValue&)*mySetStackPos->parameter("@book_id").value()) = book.bookId();
+ ((DBIntValue&)*mySetStackPos->parameter("@stack_pos").value()) = stackPos;
+ return mySetStackPos->execute();
+}
+
+bool BooksDB::insertIntoBookList(const Book &book) {
+ if (book.bookId() == 0) {
+ return false;
+ }
+ ((DBIntValue&)*myInsertBookList->parameter("@book_id").value()) = book.bookId();
+ return myInsertBookList->execute();
+}
+
+bool BooksDB::deleteFromBookList(const Book &book) {
+ if (book.bookId() == 0) {
+ return false;
+ }
+ ((DBIntValue&)*myDeleteBookList->parameter("@book_id").value()) = book.bookId();
+ return myDeleteBookList->execute();
+}
+
+bool BooksDB::checkBookList(const Book &book) {
+ if (book.bookId() == 0) {
+ return false;
+ }
+ ((DBIntValue&)*myCheckBookList->parameter("@book_id").value()) = book.bookId();
+ shared_ptr<DBValue> res = myCheckBookList->executeScalar();
+ if (res.isNull() || res->type() != DBValue::DBINT) {
+ return false;
+ }
+ const int checkRes = ((DBIntValue&)*res).value();
+ return checkRes > 0;
+}
diff --git a/fbreader/src/database/booksdb/BooksDB.h b/fbreader/src/database/booksdb/BooksDB.h
new file mode 100644
index 0000000..634b31b
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDB.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 __BOOKSDB_H__
+#define __BOOKSDB_H__
+
+#include <set>
+#include <map>
+#include <deque>
+
+#include "../sqldb/implsqlite/SQLiteDataBase.h"
+#include "DBRunnables.h"
+
+#include "../../fbreader/ReadingState.h"
+
+class Book;
+
+class BooksDB : public SQLiteDataBase {
+
+public:
+ static const std::string DATABASE_NAME;
+ static const std::string STATE_DATABASE_NAME;
+ static const std::string NET_DATABASE_NAME;
+
+ static BooksDB &Instance();
+
+private:
+ static shared_ptr<BooksDB> ourInstance;
+
+ BooksDB(const std::string &path);
+
+public:
+ virtual ~BooksDB();
+
+public:
+ bool initDatabase();
+ bool isInitialized() const;
+ bool clearDatabase();
+
+ shared_ptr<Book> loadBook(const std::string &fileName);
+ bool saveBook(const shared_ptr<Book> book);
+ bool saveAuthors(const shared_ptr<Book> book);
+ bool saveSeries(const shared_ptr<Book> book);
+ bool saveTags(const shared_ptr<Book> book);
+
+ int getFileSize(const std::string fileName);
+ bool setFileSize(const std::string fileName, int size);
+
+ bool setEncoding(const Book &book, const std::string &encoding);
+
+ bool loadFileEntries(const std::string &fileName, std::vector<std::string> &entries);
+ bool saveFileEntries(const std::string &fileName, const std::vector<std::string> &entries);
+
+ bool loadRecentBooks(std::vector<std::string> &fileNames);
+ bool saveRecentBooks(const BookList &books);
+
+ bool loadBooks(BookList &books);
+
+ bool loadBookStateStack(const Book &book, std::deque<ReadingState> &stack);
+ bool saveBookStateStack(const Book &book, const std::deque<ReadingState> &stack);
+
+ bool removeBook(const Book &book);
+
+ std::string getPalmType(const std::string &fileName);
+ bool setPalmType(const std::string &fileName, const std::string &type);
+
+ bool loadBookState(const Book &book, ReadingState &state);
+ bool setBookState(const Book &book, const ReadingState &state);
+
+ int loadStackPos(const Book &book);
+ bool setStackPos(const Book &book, int stackPos);
+
+ bool insertIntoBookList(const Book &book);
+ bool deleteFromBookList(const Book &book);
+ bool checkBookList(const Book &book);
+
+private:
+public:
+ shared_ptr<Tag> getTagById(int id) const;
+ void loadAllTagsById() const;
+
+private:
+ void loadSeries(Book &book);
+ void loadSeries(const std::map<int,shared_ptr<Book> > &books);
+ void loadAuthors(Book &book);
+ void loadAuthors(const std::map<int,shared_ptr<Book> > &books);
+ void loadTags(Book &book);
+ void loadTags(const std::map<int,shared_ptr<Book> > &books);
+
+ std::string getFileName(int fileId);
+
+private:
+ void initCommands();
+
+private:
+ bool myInitialized;
+
+ shared_ptr<SaveTableBookRunnable> mySaveTableBook;
+ shared_ptr<SaveAuthorsRunnable> mySaveAuthors;
+ shared_ptr<SaveSeriesRunnable> mySaveSeries;
+ shared_ptr<SaveTagsRunnable> mySaveTags;
+ shared_ptr<SaveBookRunnable> mySaveBook;
+ shared_ptr<SaveFileEntriesRunnable> mySaveFileEntries;
+
+ shared_ptr<FindFileIdRunnable> myFindFileId;
+
+ shared_ptr<LoadFileEntriesRunnable> myLoadFileEntries;
+
+ shared_ptr<LoadRecentBooksRunnable> myLoadRecentBooks;
+ shared_ptr<SaveRecentBooksRunnable> mySaveRecentBooks;
+
+ shared_ptr<SaveBookStateStackRunnable> mySaveBookStateStack;
+
+ shared_ptr<DeleteBookRunnable> myDeleteBook;
+
+ shared_ptr<DBCommand> myLoadNetworkLinks;
+ shared_ptr<DBCommand> myFindNetworkLinkId;
+ shared_ptr<DBCommand> myDeleteNetworkLink;
+ shared_ptr<DBCommand> myDeleteNetworkLinkUrls;
+ shared_ptr<DBCommand> myLoadNetworkLinkUrls;
+
+ shared_ptr<DBCommand> myLoadBook;
+
+ shared_ptr<DBCommand> myGetFileSize;
+ shared_ptr<DBCommand> mySetFileSize;
+
+ shared_ptr<DBCommand> myFindFileName;
+
+ shared_ptr<DBCommand> myFindAuthorId;
+
+ shared_ptr<DBCommand> myLoadBooks;
+
+ shared_ptr<DBCommand> myLoadBookStateStack;
+
+ shared_ptr<DBCommand> myGetPalmType;
+ shared_ptr<DBCommand> mySetPalmType;
+
+ shared_ptr<DBCommand> myGetNetFile;
+ shared_ptr<DBCommand> mySetNetFile;
+
+ shared_ptr<DBCommand> myLoadStackPos;
+ shared_ptr<DBCommand> mySetStackPos;
+ shared_ptr<DBCommand> myLoadBookState;
+ shared_ptr<DBCommand> mySetBookState;
+
+ shared_ptr<DBCommand> myInsertBookList;
+ shared_ptr<DBCommand> myDeleteBookList;
+ shared_ptr<DBCommand> myCheckBookList;
+
+private: // disable copying
+ BooksDB(const BooksDB &);
+ const BooksDB &operator = (const BooksDB &);
+};
+
+
+inline bool BooksDB::isInitialized() const { return myInitialized; }
+
+#endif /* __BOOKSDB_H__ */
diff --git a/fbreader/src/database/booksdb/BooksDBQuery.cpp b/fbreader/src/database/booksdb/BooksDBQuery.cpp
new file mode 100644
index 0000000..134e43b
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDBQuery.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 <ZLibrary.h>
+
+#include "BooksDBQuery.h"
+
+const std::string BooksDBQuery::ArchiveEntryDelimiter = ":";
+
+const std::string BooksDBQuery::PREINIT_DATABASE = \
+ "ATTACH @stateFile AS State; ";
+
+const std::string BooksDBQuery::INIT_DATABASE = \
+ "CREATE TABLE IF NOT EXISTS Files ( " \
+ " file_id INTEGER PRIMARY KEY, " \
+ " name TEXT NOT NULL, " \
+ " parent_id INTEGER REFERENCES Files (file_id), " \
+ " size INTEGER, " \
+ " CONSTRAINT Files_Unique UNIQUE (name, parent_id) " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS Books ( " \
+ " book_id INTEGER PRIMARY KEY, " \
+ " encoding TEXT, " \
+ " language TEXT, " \
+ " title TEXT NOT NULL, " \
+ " file_id INTEGER UNIQUE NOT NULL REFERENCES Files (file_id) " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS Authors ( " \
+ " author_id INTEGER PRIMARY KEY, " \
+ " name TEXT NOT NULL, " \
+ " sort_key TEXT NOT NULL, " \
+ " CONSTRAINT Authors_Unique UNIQUE (name, sort_key) " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS Series ( " \
+ " series_id INTEGER PRIMARY KEY, " \
+ " name TEXT UNIQUE NOT NULL " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS Tags ( " \
+ " tag_id INTEGER PRIMARY KEY, " \
+ " name TEXT NOT NULL, " \
+ " parent_id INTEGER REFERENCES Tags (tag_id), " \
+ " CONSTRAINT Tags_Unique UNIQUE (parent_id, name) " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS BookAuthor ( " \
+ " author_id INTEGER NOT NULL REFERENCES Authors (author_id), " \
+ " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \
+ " author_index INTEGER NOT NULL, " \
+ " CONSTRAINT BookAuthor_Unique0 UNIQUE (author_id, book_id), " \
+ " CONSTRAINT BookAuthor_Unique1 UNIQUE (book_id, author_index) " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS BookSeries ( " \
+ " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id), " \
+ " series_id INTEGER NOT NULL REFERENCES Series (series_id), " \
+ " book_index INTEGER " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS BookTag ( " \
+ " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \
+ " tag_id INTEGER NOT NULL REFERENCES Tags (tag_id), " \
+ " CONSTRAINT BookTag_Unique UNIQUE (book_id, tag_id) " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS State.RecentBooks ( " \
+ " book_index INTEGER PRIMARY KEY, " \
+ " book_id INTEGER UNIQUE REFERENCES Books (book_id) " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS State.BookStateStack ( " \
+ " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \
+ " position INTEGER NOT NULL, " \
+ " paragraph INTEGER NOT NULL, " \
+ " word INTEGER NOT NULL, " \
+ " char INTEGER NOT NULL, " \
+ " CONSTRAINT BookStateStack_Unique UNIQUE (book_id, position) " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS PalmType ( " \
+ " file_id INTEGER UNIQUE REFERENCES Files (file_id), " \
+ " type TEXT NOT NULL " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS State.StackPosition ( " \
+ " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id), " \
+ " stack_pos INTEGER NOT NULL " \
+ "); " \
+ " " \
+ "CREATE TABLE IF NOT EXISTS State.BookList ( " \
+ " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id) " \
+ "); ";
+
+const std::string BooksDBQuery::SECOND_INIT_DATABASE = \
+ "CREATE TRIGGER IF NOT EXISTS Books_Delete BEFORE DELETE " \
+ "ON Books FOR EACH ROW " \
+ "BEGIN " \
+ " DELETE FROM BookAuthor WHERE book_id = OLD.book_id; " \
+ " DELETE FROM BookSeries WHERE book_id = OLD.book_id; " \
+ " DELETE FROM BookTag WHERE book_id = OLD.book_id; " \
+ " DELETE FROM StackPosition WHERE book_id = OLD.book_id; " \
+ " DELETE FROM BookStateStack WHERE book_id = OLD.book_id; " \
+ " DELETE FROM RecentBooks WHERE book_id = OLD.book_id; " \
+ " DELETE FROM BookList WHERE book_id = OLD.book_id; " \
+ "END; " \
+ " " \
+ "CREATE TRIGGER IF NOT EXISTS Files_Delete BEFORE DELETE " \
+ "ON Files FOR EACH ROW " \
+ "BEGIN " \
+ " DELETE FROM Books WHERE file_id = OLD.file_id; " \
+ " DELETE FROM PalmType WHERE file_id = OLD.file_id; " \
+ "END; " \
+ " " \
+ " " \
+ "CREATE TRIGGER IF NOT EXISTS Files_Unique_Insert BEFORE INSERT " \
+ "ON Files FOR EACH ROW " \
+ "WHEN NEW.parent_id IS NULL " \
+ " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.parent_id IS NULL AND f.name = NEW.name) " \
+ "BEGIN " \
+ " SELECT RAISE(ABORT, \"columns name, parent_id are not unique\"); " \
+ "END; " \
+ " " \
+ "CREATE TRIGGER IF NOT EXISTS Files_Directory_Insert BEFORE INSERT " \
+ "ON Files FOR EACH ROW " \
+ "WHEN NEW.parent_id IS NULL AND NEW.size IS NOT NULL " \
+ "BEGIN " \
+ " SELECT RAISE(ABORT, \"size is not null for Directory entry\"); " \
+ "END; " \
+ " " \
+ "CREATE TRIGGER IF NOT EXISTS Files_ArchEntry_Insert BEFORE INSERT " \
+ "ON Files FOR EACH ROW " \
+ "WHEN NEW.parent_id IS NOT NULL " \
+ " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.file_id = NEW.parent_id AND f.parent_id IS NOT NULL) " \
+ " AND NEW.size IS NOT NULL " \
+ "BEGIN " \
+ " SELECT RAISE(ABORT, \"size is not null for Archive Entry entry\"); " \
+ "END; " \
+ " " \
+ "CREATE TRIGGER IF NOT EXISTS Files_Unique_Update BEFORE UPDATE " \
+ "ON Files FOR EACH ROW " \
+ "WHEN NEW.parent_id IS NULL " \
+ " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.parent_id IS NULL AND f.name = NEW.name AND f.file_id != NEW.file_id) " \
+ "BEGIN " \
+ " SELECT RAISE(ABORT, \"columns name, parent_id are not unique\"); " \
+ "END; " \
+ " " \
+ "CREATE TRIGGER IF NOT EXISTS Files_Directory_Update BEFORE UPDATE " \
+ "ON Files FOR EACH ROW " \
+ "WHEN NEW.parent_id IS NULL AND NEW.size IS NOT NULL " \
+ "BEGIN " \
+ " SELECT RAISE(ABORT, \"size is not null for Directory entry\"); " \
+ "END; " \
+ " " \
+ "CREATE TRIGGER IF NOT EXISTS Files_ArchEntry_Update BEFORE UPDATE " \
+ "ON Files FOR EACH ROW " \
+ "WHEN NEW.parent_id IS NOT NULL " \
+ " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.file_id = NEW.parent_id AND f.parent_id IS NOT NULL) " \
+ " AND NEW.size IS NOT NULL " \
+ "BEGIN " \
+ " SELECT RAISE(ABORT, \"size is not null for Archive Entry entry\"); " \
+ "END; ";
+
+const std::string BooksDBQuery::CLEAR_DATABASE = \
+ "DROP TRIGGER Books_Delete " \
+ "DROP TRIGGER Files_Delete " \
+ "DROP TRIGGER Files_Unique_Insert " \
+ "DROP TRIGGER Files_Directory_Insert " \
+ "DROP TRIGGER Files_ArchEntry_Insert " \
+ "DROP TRIGGER Files_Unique_Update " \
+ "DROP TRIGGER Files_Directory_Update " \
+ "DROP TRIGGER Files_ArchEntry_Update " \
+ " " \
+ "DROP TABLE State.BookList; " \
+ "DROP TABLE State.StackPosition; " \
+ "DROP TABLE PalmType; " \
+ "DROP TABLE State.BookStateStack; " \
+ "DROP TABLE State.RecentBooks; " \
+ "DROP TABLE BookTag; " \
+ "DROP TABLE BookSeries; " \
+ "DROP TABLE BookAuthor; " \
+ "DROP TABLE Tags; " \
+ "DROP TABLE Series; " \
+ "DROP TABLE Authors; " \
+ "DROP TABLE Books; " \
+ "DROP TABLE Files; ";
+
+
+const std::string BooksDBQuery::LOAD_BOOK = \
+ "SELECT " \
+ " b.book_id AS book_id, " \
+ " b.encoding AS encoding, " \
+ " b.language AS language, " \
+ " b.title AS title, " \
+ " b.file_id AS file_id " \
+ "FROM Books AS b " \
+ "WHERE b.file_id = @file_id; ";
+
+const std::string BooksDBQuery::ADD_BOOK = \
+ "INSERT INTO Books (encoding, language, title, file_id) " \
+ " VALUES ( " \
+ " nullif(@encoding,\"auto\"), " \
+ " nullif(@language,\"other\"), " \
+ " @title, " \
+ " @file_id " \
+ " ); " \
+ " " \
+ "SELECT last_insert_rowid() AS book_id; ";
+
+const std::string BooksDBQuery::UPDATE_BOOK = \
+ "UPDATE Books SET " \
+ " encoding = nullif(@encoding,\"auto\"), " \
+ " language = nullif(@language,\"other\"), " \
+ " title = @title " \
+ "WHERE " \
+ " book_id = @book_id; ";
+
+
+
+const std::string BooksDBQuery::SET_BOOK_AUTHOR = \
+ "INSERT OR REPLACE INTO BookAuthor (author_id, book_id, author_index) VALUES (@author_id, @book_id, @author_index); ";
+
+const std::string BooksDBQuery::TRIM_BOOK_AUTHORS = \
+ "DELETE FROM BookAuthor WHERE book_id = @book_id AND author_index > @authors_number; ";
+
+const std::string BooksDBQuery::FIND_AUTHOR_ID = "SELECT author_id FROM Authors WHERE name = @name AND sort_key = @sort_key; ";
+
+const std::string BooksDBQuery::ADD_AUTHOR = \
+ "INSERT INTO Authors (name, sort_key) VALUES (@name, @sort_key); " \
+ "SELECT last_insert_rowid() AS author_id; ";
+
+
+const std::string BooksDBQuery::SET_BOOKSERIES = \
+ "INSERT OR REPLACE INTO BookSeries (book_id, series_id, book_index) VALUES (" \
+ " @book_id, " \
+ " @series_id, " \
+ " nullif(@book_index, \"\") " \
+ "); ";
+
+const std::string BooksDBQuery::DELETE_BOOKSERIES = \
+ "DELETE FROM BookSeries " \
+ "WHERE " \
+ " book_id = @book_id; ";
+
+const std::string BooksDBQuery::FIND_SERIES_ID = "SELECT series_id FROM Series WHERE name = @name; ";
+
+const std::string BooksDBQuery::ADD_SERIES = \
+ "INSERT INTO Series (name) VALUES (@name); " \
+ "SELECT last_insert_rowid() AS series_id; ";
+
+
+const std::string BooksDBQuery::GET_FILE_SIZE = "SELECT size FROM Files WHERE file_id = @file_id";
+const std::string BooksDBQuery::SET_FILE_SIZE = "UPDATE Files SET size = @size WHERE file_id = @file_id";
+
+const std::string BooksDBQuery::SET_ENCODING = "UPDATE Books SET encoding = @encoding WHERE book_id = @book_id; ";
+
+const std::string BooksDBQuery::LOAD_FILE_ENTRIES = "SELECT name FROM Files WHERE coalesce(parent_id, 0) = @file_id; ";
+
+const std::string BooksDBQuery::INVALIDATE_BOOKS = "UPDATE Books SET title = \"\" WHERE file_id = @file_id; ";
+
+
+const std::string BooksDBQuery::DELETE_FILE = "DELETE FROM Files WHERE file_id = @file_id; ";
+
+const std::string BooksDBQuery::DELETE_FILE_ENTRIES = "DELETE FROM Files WHERE parent_id = @file_id; ";
+
+const std::string BooksDBQuery::LOAD_FILE_ENTRY_IDS = "SELECT file_id FROM Files WHERE coalesce(parent_id, 0) = @file_id; ";
+
+const std::string BooksDBQuery::FIND_BOOK_ID = "SELECT book_id FROM Books WHERE file_id = @file_id; ";
+
+
+const std::string BooksDBQuery::LOAD_RECENT_BOOKS = \
+ "SELECT b.file_id " \
+ "FROM Books AS b " \
+ " INNER JOIN RecentBooks AS rb ON b.book_id = rb.book_id " \
+ "ORDER BY rb.book_index; ";
+
+const std::string BooksDBQuery::CLEAR_RECENT_BOOKS = "DELETE FROM RecentBooks; ";
+
+const std::string BooksDBQuery::INSERT_RECENT_BOOKS = "INSERT INTO RecentBooks (book_id) VALUES (@book_id); ";
+
+const std::string BooksDBQuery::FIND_FILE_NAME = "SELECT name, parent_id FROM Files WHERE file_id = @file_id; ";
+
+const std::string BooksDBQuery::LOAD_BOOKS = "SELECT book_id, encoding, language, title, file_id FROM Books; ";
+
+const std::string BooksDBQuery::UPDATE_AUTHOR = \
+ "UPDATE Authors SET " \
+ " name = @newName, " \
+ " sort_key = @newSortKey " \
+ "WHERE name = @oldName " \
+ " AND sort_key = @oldSortKey; ";
+
+const std::string BooksDBQuery::UPDATE_BOOKS_AUTHOR = "UPDATE OR REPLACE BookAuthor SET author_id = @newAuthorId WHERE author_id = @oldAuthorId; ";
+
+const std::string BooksDBQuery::LOAD_BOOK_STATE_STACK = "SELECT paragraph, word, char FROM BookStateStack WHERE book_id = @book_id AND position > 0 ORDER BY position; ";
+const std::string BooksDBQuery::TRIM_BOOK_STATE_STACK = "DELETE FROM BookStateStack WHERE book_id = @book_id AND position > @stackSize; ";
+const std::string BooksDBQuery::SET_BOOK_STATE_STACK = "INSERT OR REPLACE INTO BookStateStack(book_id, position, paragraph, word, char) VALUES (@book_id, @position, @paragraph, @word, @char); ";
+
+const std::string BooksDBQuery::SET_PALM_TYPE = "INSERT OR REPLACE INTO PalmType (file_id, type) VALUES (@file_id, @type); ";
+const std::string BooksDBQuery::GET_PALM_TYPE = "SELECT type FROM PalmType WHERE file_id = @file_id; ";
+
+const std::string BooksDBQuery::LOAD_STACK_POS = "SELECT stack_pos FROM StackPosition WHERE book_id = @book_id; ";
+const std::string BooksDBQuery::SET_STACK_POS = "INSERT OR REPLACE INTO StackPosition(book_id, stack_pos) VALUES (@book_id, @stack_pos); ";
+
+const std::string BooksDBQuery::LOAD_BOOK_STATE = "SELECT paragraph, word, char FROM BookStateStack WHERE book_id = @book_id AND position = 0; ";
+const std::string BooksDBQuery::SET_BOOK_STATE = "INSERT OR REPLACE INTO BookStateStack(book_id, position, paragraph, word, char) VALUES (@book_id, 0, @paragraph, @word, @char); ";
+
+const std::string BooksDBQuery::INSERT_BOOK_LIST = "INSERT OR IGNORE INTO BookList(book_id) VALUES (@book_id); ";
+const std::string BooksDBQuery::DELETE_BOOK_LIST = "DELETE FROM BookList WHERE book_id = @book_id; ";
+const std::string BooksDBQuery::CHECK_BOOK_LIST = "SELECT COUNT(*) FROM BookList WHERE book_id = @book_id; ";
diff --git a/fbreader/src/database/booksdb/BooksDBQuery.h b/fbreader/src/database/booksdb/BooksDBQuery.h
new file mode 100644
index 0000000..c202dea
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDBQuery.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 __BOOKSDBQUERY_H__
+#define __BOOKSDBQUERY_H__
+
+#include <string>
+
+class BooksDBQuery {
+
+public:
+ static const std::string ArchiveEntryDelimiter;
+
+public:
+ static const std::string PREINIT_DATABASE;
+ static const std::string INIT_DATABASE;
+ static const std::string SECOND_INIT_DATABASE;
+ static const std::string CLEAR_DATABASE;
+
+ static const std::string LOAD_BOOK;
+
+ static const std::string ADD_BOOK;
+ static const std::string UPDATE_BOOK;
+
+ static const std::string FIND_AUTHOR_ID;
+ static const std::string ADD_AUTHOR;
+
+ static const std::string GET_BOOK_AUTHORS_NUMBER;
+ static const std::string TRIM_BOOK_AUTHORS;
+ static const std::string SET_BOOK_AUTHOR;
+
+ static const std::string DELETE_BOOKSERIES;
+ static const std::string FIND_SERIES_ID;
+ static const std::string ADD_SERIES;
+ static const std::string SET_BOOKSERIES;
+
+ static const std::string GET_FILE_SIZE;
+ static const std::string SET_FILE_SIZE;
+
+ static const std::string SET_ENCODING;
+
+ static const std::string LOAD_FILE_ENTRIES;
+
+ static const std::string INVALIDATE_BOOKS;
+
+ static const std::string DELETE_FILE;
+ static const std::string DELETE_FILE_ENTRIES;
+ static const std::string LOAD_FILE_ENTRY_IDS;
+
+ static const std::string FIND_BOOK_ID;
+
+ static const std::string LOAD_RECENT_BOOKS;
+ static const std::string CLEAR_RECENT_BOOKS;
+ static const std::string INSERT_RECENT_BOOKS;
+
+ static const std::string FIND_FILE_NAME;
+
+ static const std::string LOAD_BOOKS;
+
+ static const std::string UPDATE_AUTHOR;
+ static const std::string UPDATE_BOOKS_AUTHOR;
+
+ static const std::string LOAD_BOOK_STATE_STACK;
+ static const std::string TRIM_BOOK_STATE_STACK;
+ static const std::string SET_BOOK_STATE_STACK;
+
+ static const std::string GET_PALM_TYPE;
+ static const std::string SET_PALM_TYPE;
+
+ static const std::string LOAD_BOOK_STATE;
+ static const std::string SET_BOOK_STATE;
+ static const std::string LOAD_STACK_POS;
+ static const std::string SET_STACK_POS;
+
+ static const std::string INSERT_BOOK_LIST;
+ static const std::string DELETE_BOOK_LIST;
+ static const std::string CHECK_BOOK_LIST;
+
+private: // disable creation Instances
+ BooksDBQuery();
+};
+
+#endif /* __BOOKSDBQUERY_H__ */
diff --git a/fbreader/src/database/booksdb/BooksDBUtil.cpp b/fbreader/src/database/booksdb/BooksDBUtil.cpp
new file mode 100644
index 0000000..3a7de96
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDBUtil.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 <ZLFile.h>
+#include <ZLDir.h>
+#include <ZLStringUtil.h>
+
+#include "BooksDBUtil.h"
+#include "BooksDB.h"
+
+#include "../../library/Book.h"
+#include "../../library/Tag.h"
+#include "../../library/Author.h"
+
+shared_ptr<Book> BooksDBUtil::getBook(const std::string &filePath, bool checkFile) {
+ const std::string physicalFilePath = ZLFile(filePath).physicalFilePath();
+
+ ZLFile file(physicalFilePath);
+ if (checkFile && !file.exists()) {
+ return 0;
+ }
+
+ if (!checkFile || checkInfo(file)) {
+ shared_ptr<Book> book = loadFromDB(filePath);
+ if (!book.isNull() && isBookFull(*book)) {
+ return book;
+ }
+ } else {
+ if (physicalFilePath != filePath) {
+ resetZipInfo(file);
+ }
+ saveInfo(file);
+ }
+
+ shared_ptr<Book> book = Book::loadFromFile(ZLFile(filePath));
+ if (book.isNull()) {
+ return 0;
+ }
+ BooksDB::Instance().saveBook(book);
+ return book;
+}
+
+bool BooksDBUtil::getRecentBooks(BookList &books) {
+ std::vector<std::string> fileNames;
+ if (!BooksDB::Instance().loadRecentBooks(fileNames)) {
+ return false;
+ }
+ for (std::vector<std::string>::const_iterator it = fileNames.begin(); it != fileNames.end(); ++it) {
+ shared_ptr<Book> book = getBook(*it /*, true OR false ? */); // TODO: check file ???
+ if (!book.isNull()) {
+ books.push_back(book);
+ }
+ }
+ return true;
+}
+
+bool BooksDBUtil::getBooks(std::map<std::string, shared_ptr<Book> > &booksmap, bool checkFile) {
+ BookList books;
+ if (!BooksDB::Instance().loadBooks(books)) {
+ return false;
+ }
+ for (BookList::iterator it = books.begin(); it != books.end(); ++it) {
+ Book &book = **it;
+ const std::string physicalFilePath = book.file().physicalFilePath();
+ ZLFile file(physicalFilePath);
+ if (!checkFile || file.exists()) {
+ if (!checkFile || checkInfo(file)) {
+ if (isBookFull(book)) {
+ booksmap.insert(std::make_pair(book.file().path(), *it));
+ continue;
+ }
+ } else {
+ if (physicalFilePath != book.file().path()) {
+ resetZipInfo(file);
+ }
+ saveInfo(file);
+ }
+ shared_ptr<Book> bookptr = Book::loadFromFile(book.file());
+ if (!bookptr.isNull()) {
+ BooksDB::Instance().saveBook(bookptr);
+ booksmap.insert(std::make_pair(book.file().path(), bookptr));
+ }
+ }
+ }
+ return true;
+}
+
+bool BooksDBUtil::isBookFull(const Book &book) {
+ return
+ !book.title().empty() &&
+ !book.encoding().empty();
+}
+
+shared_ptr<Book> BooksDBUtil::loadFromDB(const std::string &filePath) {
+ if (filePath.empty()) {
+ return 0;
+ }
+ return BooksDB::Instance().loadBook(filePath);
+}
+
+bool BooksDBUtil::checkInfo(const ZLFile &file) {
+ return BooksDB::Instance().getFileSize(file.path()) == (int) file.size();
+}
+
+void BooksDBUtil::saveInfo(const ZLFile &file) {
+ BooksDB::Instance().setFileSize(file.path(), file.size());
+}
+
+void BooksDBUtil::listZipEntries(const ZLFile &zipFile, std::vector<std::string> &entries) {
+ entries.clear();
+ BooksDB::Instance().loadFileEntries(zipFile.path(), entries);
+ if (entries.empty()) {
+ resetZipInfo(zipFile);
+ BooksDB::Instance().loadFileEntries(zipFile.path(), entries);
+ }
+}
+
+void BooksDBUtil::resetZipInfo(const ZLFile &zipFile) {
+ shared_ptr<ZLDir> zipDir = zipFile.directory();
+ if (!zipDir.isNull()) {
+ std::vector<std::string> entries;
+ zipDir->collectFiles(entries, false);
+ BooksDB::Instance().saveFileEntries(zipFile.path(), entries);
+ }
+}
+
+bool BooksDBUtil::canRemoveFile(const std::string &filePath) {
+ ZLFile bookFile(filePath);
+ std::string physicalPath = bookFile.physicalFilePath();
+ if (filePath != physicalPath) {
+ ZLFile zipFile(physicalPath);
+ shared_ptr<ZLDir> zipDir = zipFile.directory();
+ if (zipDir.isNull()) {
+ return false;
+ }
+ std::vector<std::string> entries;
+ zipDir->collectFiles(entries, false); // TODO: replace with BooksDB call???
+ if (entries.size() != 1) {
+ return false;
+ }
+ if (zipDir->itemPath(entries[0]) != filePath) {
+ return false;
+ }
+ }
+ return ZLFile(physicalPath).canRemove();
+}
+
+void BooksDBUtil::addTag(shared_ptr<Book> book, shared_ptr<Tag> tag) {
+ if (book->addTag(tag)) {
+ BooksDB::Instance().saveTags(book);
+ }
+}
+
+void BooksDBUtil::renameTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) {
+ if (book->renameTag(from, to, includeSubTags)) {
+ BooksDB::Instance().saveTags(book);
+ }
+}
+
+void BooksDBUtil::cloneTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) {
+ if (book->cloneTag(from, to, includeSubTags)) {
+ BooksDB::Instance().saveTags(book);
+ }
+}
+
+void BooksDBUtil::removeAllTags(shared_ptr<Book> book) {
+ book->removeAllTags();
+}
diff --git a/fbreader/src/database/booksdb/BooksDBUtil.h b/fbreader/src/database/booksdb/BooksDBUtil.h
new file mode 100644
index 0000000..8d55e1c
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDBUtil.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 __BOOKSDBUTIL_H__
+#define __BOOKSDBUTIL_H__
+
+#include <map>
+
+#include <shared_ptr.h>
+
+#include "../../library/Lists.h"
+
+class Book;
+class ZLFile;
+
+class BooksDBUtil {
+
+public:
+ static shared_ptr<Book> getBook(const std::string &fileName, bool checkFile = true);
+
+ static bool getBooks(std::map<std::string, shared_ptr<Book> > &booksmap, bool checkFile = true);
+
+ static bool getRecentBooks(BookList &books);
+
+public:
+ static void addTag(shared_ptr<Book> book, shared_ptr<Tag> tag);
+ static void renameTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags);
+ static void cloneTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags);
+ static void removeAllTags(shared_ptr<Book> book);
+
+ static bool checkInfo(const ZLFile &file);
+ static void saveInfo(const ZLFile &file);
+
+ static void listZipEntries(const ZLFile &zipFile, std::vector<std::string> &entries);
+ static void resetZipInfo(const ZLFile &zipFile);
+
+ static bool isBookFull(const Book &book);
+
+ static bool canRemoveFile(const std::string &fileName);
+
+private:
+ static shared_ptr<Book> loadFromDB(const std::string &fileName);
+};
+
+#endif /* __BOOKSDBUTIL_H__ */
diff --git a/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp b/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp
new file mode 100644
index 0000000..8cdc2a4
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 <string>
+
+#include "BooksDB.h"
+#include "../../library/Book.h"
+#include "../../library/Author.h"
+#include "../sqldb/implsqlite/SQLiteFactory.h"
+
+static const std::string LOAD_AUTHORS_QUERY =
+ "SELECT Authors.name, Authors.sort_key" \
+ " FROM BookAuthor" \
+ " INNER JOIN Authors ON Authors.author_id = BookAuthor.author_id" \
+ " WHERE BookAuthor.book_id = @book_id" \
+ " ORDER BY BookAuthor.author_index;";
+static const std::string LOAD_ALL_AUTHORS_QUERY =
+ "SELECT Authors.name, Authors.sort_key, BookAuthor.book_id" \
+ " FROM BookAuthor" \
+ " INNER JOIN Authors ON Authors.author_id = BookAuthor.author_id" \
+ " ORDER BY BookAuthor.author_index;";
+
+void BooksDB::loadAuthors(Book &book) {
+ static shared_ptr<DBCommand> command = SQLiteFactory::createCommand(
+ LOAD_AUTHORS_QUERY, connection(), "@book_id", DBValue::DBINT
+ );
+ ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId();
+ shared_ptr<DBDataReader> reader = command->executeReader();
+
+ book.removeAllAuthors();
+
+ while (reader->next()) {
+ book.addAuthor(
+ reader->textValue(0, std::string()),
+ reader->textValue(1, std::string())
+ );
+ }
+}
+
+void BooksDB::loadAuthors(const std::map<int,shared_ptr<Book> > &books) {
+ static shared_ptr<DBCommand> command = SQLiteFactory::createCommand(
+ LOAD_ALL_AUTHORS_QUERY, connection()
+ );
+ shared_ptr<DBDataReader> reader = command->executeReader();
+
+ for (std::map<int,shared_ptr<Book> >::const_iterator it = books.begin(); it != books.end(); ++it) {
+ it->second->removeAllAuthors();
+ }
+
+ while (reader->next()) {
+ std::map<int,shared_ptr<Book> >::const_iterator it =
+ books.find((reader->type(2) == DBValue::DBINT) ? reader->intValue(2) : 0);
+ if (it != books.end()) {
+ it->second->addAuthor(
+ reader->textValue(0, std::string()),
+ reader->textValue(1, std::string())
+ );
+ }
+ }
+}
diff --git a/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp b/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp
new file mode 100644
index 0000000..a9b860b
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 <string>
+
+#include "BooksDB.h"
+#include "../../library/Book.h"
+#include "../sqldb/implsqlite/SQLiteFactory.h"
+
+static const std::string LOAD_SERIES_QUERY =
+ "SELECT Series.name, BookSeries.book_index" \
+ " FROM BookSeries" \
+ " INNER JOIN Series ON Series.series_id = BookSeries.series_id" \
+ " WHERE BookSeries.book_id = @book_id;";
+static const std::string LOAD_ALL_SERIES_QUERY =
+ "SELECT Series.name, BookSeries.book_index, BookSeries.book_id" \
+ " FROM BookSeries" \
+ " INNER JOIN Series ON Series.series_id = BookSeries.series_id";
+
+static Number getSeriesIndex(shared_ptr<DBDataReader> reader) {
+ Number seriesIndex;
+ if (reader->type(1) == DBValue::DBTEXT) {
+ seriesIndex = Number(reader->textValue(1, std::string()));
+ } else if (reader->type(1) == DBValue::DBREAL){ //for old database scheme
+ seriesIndex = Number((int)reader->realValue(1));
+ } else { //for old database scheme
+ seriesIndex = Number(reader->intValue(1));
+ }
+ return seriesIndex;
+}
+
+void BooksDB::loadSeries(Book &book) {
+ static shared_ptr<DBCommand> command = SQLiteFactory::createCommand(
+ LOAD_SERIES_QUERY, connection(), "@book_id", DBValue::DBINT
+ );
+ ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId();
+ shared_ptr<DBDataReader> reader = command->executeReader();
+
+ if (reader->next()) {
+ std::string seriesTitle = reader->textValue(0, std::string());
+ if (!seriesTitle.empty()) {
+ book.setSeries(
+ seriesTitle,
+ getSeriesIndex(reader)
+ );
+ }
+ }
+}
+
+void BooksDB::loadSeries(const std::map<int,shared_ptr<Book> > &books) {
+ shared_ptr<DBCommand> command = SQLiteFactory::createCommand(
+ LOAD_ALL_SERIES_QUERY, connection()
+ );
+ shared_ptr<DBDataReader> reader = command->executeReader();
+
+ while (reader->next()) {
+ std::string seriesTitle = reader->textValue(0, std::string());
+ std::map<int,shared_ptr<Book> >::const_iterator it =
+ books.find((reader->type(2) == DBValue::DBINT) ? reader->intValue(2) : 0);
+ if (!seriesTitle.empty() && it != books.end()) {
+ it->second->setSeries(
+ seriesTitle,
+ getSeriesIndex(reader)
+ );
+ }
+ }
+}
diff --git a/fbreader/src/database/booksdb/BooksDB_BookTag.cpp b/fbreader/src/database/booksdb/BooksDB_BookTag.cpp
new file mode 100644
index 0000000..a3d36fe
--- /dev/null
+++ b/fbreader/src/database/booksdb/BooksDB_BookTag.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 <string>
+#include <algorithm>
+
+#include "BooksDB.h"
+#include "DBRunnables.h"
+#include "../../library/Book.h"
+#include "../../library/Tag.h"
+#include "../sqldb/implsqlite/SQLiteFactory.h"
+
+static const std::string LOAD_BOOK_TAGS_QUERY =
+ "SELECT tag_id FROM BookTag WHERE book_id = @book_id";
+static const std::string LOAD_ALL_BOOK_TAGS_QUERY =
+ "SELECT tag_id, book_id FROM BookTag";
+static const std::string LOAD_SINGLE_TAG_QUERY =
+ "SELECT name, parent_id FROM Tags WHERE tag_id = @tag_id";
+static const std::string LOAD_ALL_TAGS_QUERY =
+ "SELECT name, parent_id, tag_id FROM Tags ORDER BY tag_id";
+static const std::string ADD_BOOKTAG =
+ "INSERT INTO BookTag (book_id, tag_id) VALUES (@book_id, @tag_id)";
+static const std::string DELETE_BOOKTAG =
+ "DELETE FROM BookTag WHERE book_id = @book_id AND tag_id = @tag_id";
+static const std::string FIND_TAG_ID =
+ "SELECT tag_id FROM Tags" \
+ " WHERE name = @name AND coalesce(parent_id, 0) = @parent_id";
+static const std::string ADD_TAG =
+ "INSERT INTO Tags (name, parent_id) VALUES (@name, nullif(@parent_id, 0));" \
+ " SELECT last_insert_rowid() AS tag_id";
+
+void BooksDB::loadTags(Book &book) {
+ static shared_ptr<DBCommand> command = SQLiteFactory::createCommand(
+ LOAD_BOOK_TAGS_QUERY, connection(), "@book_id", DBValue::DBINT
+ );
+ ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId();
+ shared_ptr<DBDataReader> reader = command->executeReader();
+
+ book.removeAllTags();
+ while (reader->next()) {
+ book.addTag(getTagById(reader->intValue(0)));
+ }
+}
+
+void BooksDB::loadTags(const std::map<int,shared_ptr<Book> > &books) {
+ loadAllTagsById();
+
+ static shared_ptr<DBCommand> command = SQLiteFactory::createCommand(
+ LOAD_ALL_BOOK_TAGS_QUERY, connection()
+ );
+ shared_ptr<DBDataReader> reader = command->executeReader();
+
+ for (std::map<int,shared_ptr<Book> >::const_iterator it = books.begin(); it != books.end(); ++it) {
+ it->second->removeAllTags();
+ }
+
+ while (reader->next()) {
+ std::map<int,shared_ptr<Book> >::const_iterator it =
+ books.find((reader->type(1) == DBValue::DBINT) ? reader->intValue(1) : 0);
+ if (it != books.end()) {
+ it->second->addTag(getTagById(reader->intValue(0)));
+ }
+ }
+}
+
+shared_ptr<Tag> BooksDB::getTagById(int id) const {
+ if (id == 0) {
+ return 0;
+ }
+
+ shared_ptr<Tag> tag = Tag::getTagById(id);
+ if (!tag.isNull()) {
+ return tag;
+ }
+
+ static shared_ptr<DBCommand> command = SQLiteFactory::createCommand(
+ LOAD_SINGLE_TAG_QUERY, connection(), "@tag_id", DBValue::DBINT
+ );
+ ((DBIntValue&)*command->parameter("@tag_id").value()) = id;
+ shared_ptr<DBDataReader> reader = command->executeReader();
+ if (!reader->next()) {
+ return 0;
+ }
+ const std::string name = reader->textValue(0, std::string());
+ const int parentId = (reader->type(1) == DBValue::DBINT) ?
+ reader->intValue(1) : 0;
+ reader.reset();
+
+ return Tag::getTag(name, getTagById(parentId), id);
+}
+
+void BooksDB::loadAllTagsById() const {
+ static shared_ptr<DBCommand> command = SQLiteFactory::createCommand(
+ LOAD_ALL_TAGS_QUERY, connection()
+ );
+ shared_ptr<DBDataReader> reader = command->executeReader();
+ while (reader->next()) {
+ if (reader->type(2) != DBValue::DBINT) {
+ continue;
+ }
+ const int id = reader->intValue(2);
+ if (!Tag::getTagById(id).isNull()) {
+ continue;
+ }
+ Tag::getTag(
+ reader->textValue(0, std::string()),
+ Tag::getTagById(
+ (reader->type(1) == DBValue::DBINT) ? reader->intValue(1) : 0
+ ),
+ id
+ );
+ }
+}
+
+SaveTagsRunnable::SaveTagsRunnable(DBConnection &connection) {
+ myDeleteBookTag = SQLiteFactory::createCommand(
+ DELETE_BOOKTAG, connection,
+ "@book_id", DBValue::DBINT,
+ "@tag_id", DBValue::DBINT
+ );
+ myFindTagId = SQLiteFactory::createCommand(
+ FIND_TAG_ID, connection,
+ "@name", DBValue::DBTEXT,
+ "@parent_id", DBValue::DBINT
+ );
+ myAddTag = SQLiteFactory::createCommand(
+ ADD_TAG, connection,
+ "@name", DBValue::DBTEXT,
+ "@parent_id", DBValue::DBINT
+ );
+ myAddBookTag = SQLiteFactory::createCommand(
+ ADD_BOOKTAG, connection,
+ "@book_id", DBValue::DBINT,
+ "@tag_id", DBValue::DBINT
+ );
+ myLoadBookTags = SQLiteFactory::createCommand(
+ LOAD_BOOK_TAGS_QUERY, connection, "@book_id", DBValue::DBINT
+ );
+}
+
+bool SaveTagsRunnable::run() {
+ if (myBook->bookId() == 0) {
+ return false;
+ }
+
+ ((DBIntValue&)*myDeleteBookTag->parameter("@book_id").value()) = myBook->bookId();
+ DBIntValue &delTagId = (DBIntValue&)*myDeleteBookTag->parameter("@tag_id").value();
+ ((DBIntValue&)*myAddBookTag->parameter("@book_id").value()) = myBook->bookId();
+ DBIntValue &addTagId = (DBIntValue&)*myAddBookTag->parameter("@tag_id").value();
+
+ TagList dbTags;
+
+ ((DBIntValue&)*myLoadBookTags->parameter("@book_id").value()) = myBook->bookId();
+ shared_ptr<DBDataReader> reader = myLoadBookTags->executeReader();
+
+ while (reader->next()) {
+ shared_ptr<Tag> tag = BooksDB::Instance().getTagById(reader->intValue(0));
+ if (!tag.isNull()) {
+ dbTags.push_back(tag);
+ }
+ }
+
+ TagList tags = myBook->tags(); // make copy of vector
+
+ for (TagList::const_iterator it = dbTags.begin(); it != dbTags.end(); ++it) {
+ shared_ptr<Tag> tag = (*it);
+ findTagId(tag);
+ TagList::iterator jt = std::find(tags.begin(), tags.end(), tag);
+ if (jt == tags.end()) {
+ // Tag `tag` must be removed from BookTag table.
+ delTagId.setValue(tag->tagId());
+ if (!myDeleteBookTag->execute()) {
+ return false;
+ }
+ } else {
+ // This tag is already in DataBase => need not to be inserted.
+ tags.erase(jt);
+ }
+ }
+
+ for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) {
+ int tableTagId = findTagId(*it);
+ if (tableTagId == 0) {
+ return false;
+ }
+ addTagId.setValue(tableTagId);
+ if (!myAddBookTag->execute()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+int SaveTagsRunnable::findTagId(shared_ptr<Tag> tag) {
+ int tagId = tag->tagId();
+ if (tagId != 0) {
+ return tagId;
+ }
+ int parentId = 0;
+ if (!tag->parent().isNull()) {
+ parentId = findTagId(tag->parent());
+ if (parentId == 0) {
+ return 0;
+ }
+ }
+
+ DBIntValue &findParent = (DBIntValue&)*myFindTagId->parameter("@parent_id").value();
+ DBTextValue &findName = (DBTextValue&)*myFindTagId->parameter("@name").value();
+ DBIntValue &addParent = (DBIntValue&)*myAddTag->parameter("@parent_id").value();
+ DBTextValue &addName = (DBTextValue&)*myAddTag->parameter("@name").value();
+
+ findParent.setValue(parentId);
+ findName.setValue(tag->name());
+ shared_ptr<DBValue> tableTagId = myFindTagId->executeScalar();
+ if (tableTagId.isNull() || tableTagId->type() != DBValue::DBINT || ((DBIntValue&)*tableTagId).value() == 0) {
+ addParent.setValue(parentId);
+ addName.setValue(tag->name());
+ tableTagId = myAddTag->executeScalar();
+ if (tableTagId.isNull() || tableTagId->type() != DBValue::DBINT || ((DBIntValue&)*tableTagId).value() == 0) {
+ return 0;
+ }
+ }
+ Tag::setTagId(tag, ((DBIntValue&)*tableTagId).value());
+ return ((DBIntValue&)*tableTagId).value();
+}
+
+void SaveTagsRunnable::setBook(shared_ptr<Book> book) {
+ myBook = book;
+}
diff --git a/fbreader/src/database/booksdb/DBRunnables.h b/fbreader/src/database/booksdb/DBRunnables.h
new file mode 100644
index 0000000..4baee65
--- /dev/null
+++ b/fbreader/src/database/booksdb/DBRunnables.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 __DBRUNNABLES_H__
+#define __DBRUNNABLES_H__
+
+#include <deque>
+#include <vector>
+
+#include "../../fbreader/ReadingState.h"
+
+#include "../sqldb/DBConnection.h"
+#include "../sqldb/DBCommand.h"
+#include "../sqldb/DBRunnable.h"
+
+#include "BooksDBQuery.h"
+
+#include "../../library/Lists.h"
+
+class FindFileIdRunnable;
+class LoadFileEntriesRunnable;
+class DeleteFileEntriesRunnable;
+
+/*
+ * Save Runnables
+ */
+
+
+class InitBooksDBRunnable : public DBRunnable {
+
+public:
+ InitBooksDBRunnable(DBConnection &connection);
+ bool run();
+
+private:
+ DBConnection &myConnection;
+};
+
+class ClearBooksDBRunnable : public DBRunnable {
+
+public:
+ ClearBooksDBRunnable(DBConnection &connection);
+ bool run();
+
+private:
+ DBConnection &myConnection;
+};
+
+class SaveTableBookRunnable : public DBRunnable {
+
+public:
+ SaveTableBookRunnable(DBConnection &connection);
+ bool run();
+ void setBook(shared_ptr<Book> book);
+
+private:
+ bool addTableBook(const shared_ptr<Book> book, int fileId);
+ bool updateTableBook(const shared_ptr<Book> book);
+
+private:
+ shared_ptr<Book> myBook;
+
+ shared_ptr<DBCommand> myFindBookId;
+
+ shared_ptr<DBCommand> myAddBook;
+ shared_ptr<DBCommand> myUpdateBook;
+
+ shared_ptr<FindFileIdRunnable> myFindFileId;
+};
+
+class SaveAuthorsRunnable : public DBRunnable {
+
+public:
+ SaveAuthorsRunnable(DBConnection &connection);
+ bool run();
+ void setBook(shared_ptr<Book> book);
+
+private:
+ shared_ptr<Book> myBook;
+
+ shared_ptr<DBCommand> mySetBookAuthor;
+ shared_ptr<DBCommand> myTrimBookAuthors;
+
+ shared_ptr<DBCommand> myFindAuthorId;
+ shared_ptr<DBCommand> myAddAuthor;
+};
+
+class SaveTagsRunnable : public DBRunnable {
+
+public:
+ SaveTagsRunnable(DBConnection &connection);
+ bool run();
+ void setBook(shared_ptr<Book> book);
+
+private:
+ int findTagId(shared_ptr<Tag> tag);
+
+private:
+ shared_ptr<Book> myBook;
+
+ shared_ptr<DBCommand> myAddBookTag;
+ shared_ptr<DBCommand> myDeleteBookTag;
+
+ shared_ptr<DBCommand> myFindTagId;
+ shared_ptr<DBCommand> myAddTag;
+
+ shared_ptr<DBCommand> myLoadBookTags;
+};
+
+
+class SaveSeriesRunnable : public DBRunnable {
+
+public:
+ SaveSeriesRunnable(DBConnection &connection);
+ bool run();
+ void setBook(shared_ptr<Book> book);
+
+private:
+ shared_ptr<Book> myBook;
+
+ shared_ptr<DBCommand> mySetBookSeries;
+ shared_ptr<DBCommand> myDeleteBookSeries;
+
+ shared_ptr<DBCommand> myFindSeriesId;
+ shared_ptr<DBCommand> myAddSeries;
+};
+
+class SaveBookRunnable : public DBRunnable {
+public:
+ SaveBookRunnable(SaveTableBookRunnable &saveTableBook, SaveAuthorsRunnable &saveAuthors,
+ SaveSeriesRunnable &saveSeries, SaveTagsRunnable &saveTags);
+
+ bool run();
+ void setBook(shared_ptr<Book> book);
+
+private:
+ SaveTableBookRunnable &mySaveTableBook;
+ SaveAuthorsRunnable &mySaveAuthors;
+ SaveSeriesRunnable &mySaveSeries;
+ SaveTagsRunnable &mySaveTags;
+};
+
+class SaveFileEntriesRunnable : public DBRunnable {
+
+public:
+ SaveFileEntriesRunnable(DBConnection &connection);
+ bool run();
+ void setEntries(const std::string &fileName, const std::vector<std::string> &entries);
+
+private:
+ std::string myFileName;
+ std::vector<std::string> myEntries;
+
+ shared_ptr<DBCommand> myAddFile;
+
+ shared_ptr<FindFileIdRunnable> myFindFileId;
+ shared_ptr<DeleteFileEntriesRunnable> myDeleteFileEntries;
+};
+
+class SaveRecentBooksRunnable : public DBRunnable {
+
+public:
+ SaveRecentBooksRunnable(DBConnection &connection);
+ bool run();
+ void setBooks(const BookList &books);
+
+private:
+ BookList myBooks;
+
+ shared_ptr<DBCommand> myClearRecentBooks;
+ shared_ptr<DBCommand> myInsertRecentBooks;
+};
+
+class SaveBookStateStackRunnable : public DBRunnable {
+
+public:
+ SaveBookStateStackRunnable(DBConnection &connection);
+ bool run();
+ void setState(int bookId, const std::deque<ReadingState > &stack);
+
+private:
+ int myBookId;
+ std::deque<ReadingState > myStack;
+
+ shared_ptr<DBCommand> myTrimBookStateStack;
+ shared_ptr<DBCommand> mySetBookStateStack;
+};
+
+class DeleteFileEntriesRunnable : public DBRunnable {
+
+public:
+ DeleteFileEntriesRunnable(DBConnection &connection);
+ bool run();
+ void setFileId(int fileId);
+
+private:
+ bool doDelete(int fileId);
+
+private:
+ int myFileId;
+
+ shared_ptr<DBCommand> myDeleteFileEntries;
+ shared_ptr<DBCommand> myLoadFileEntryIds;
+};
+
+class DeleteBookRunnable : public DBRunnable {
+
+public:
+ DeleteBookRunnable(DBConnection &connection);
+ bool run();
+ void setFileName(const std::string &fileName);
+
+private:
+ std::string myFileName;
+
+ shared_ptr<FindFileIdRunnable> myFindFileId;
+ shared_ptr<DBCommand> myDeleteFile;
+};
+
+
+inline InitBooksDBRunnable::InitBooksDBRunnable(DBConnection &connection) : myConnection(connection) {}
+inline ClearBooksDBRunnable::ClearBooksDBRunnable(DBConnection &connection) : myConnection(connection) {}
+
+inline void SaveFileEntriesRunnable::setEntries(const std::string &fileName, const std::vector<std::string> &entries) {
+ myFileName = fileName;
+ myEntries = entries; // copy vector
+}
+
+inline void SaveBookStateStackRunnable::setState(int bookId, const std::deque<ReadingState > &stack) {
+ myBookId = bookId;
+ myStack = stack; // copy deque
+}
+
+inline void DeleteFileEntriesRunnable::setFileId(int fileId) { myFileId = fileId; }
+
+inline void DeleteBookRunnable::setFileName(const std::string &fileName) { myFileName = fileName; }
+
+/*
+ * Load & Modify Runnables
+ */
+
+class FindFileIdRunnable : public DBRunnable {
+
+public:
+ FindFileIdRunnable(DBConnection &connection);
+ bool run();
+ void setFileName(const std::string &fileName, bool add = false);
+
+ int fileId() const;
+
+private:
+ std::string myFileName;
+ bool myAdd;
+ int myFileId;
+
+ shared_ptr<DBCommand> myFindFileId;
+ shared_ptr<DBCommand> myAddFile;
+};
+
+
+/*
+ * Load Runnables
+ */
+
+class LoadFileEntriesRunnable : public DBRunnable {
+
+public:
+ LoadFileEntriesRunnable(DBConnection &connection);
+ bool run();
+ void setFileName(const std::string &fileName);
+ void collectEntries(std::vector<std::string> &entries);
+
+private:
+ std::string myFileName;
+ std::vector<std::string> myEntries;
+
+ shared_ptr<FindFileIdRunnable> myFindFileId;
+
+ shared_ptr<DBCommand> myLoadFileEntries;
+};
+
+class LoadRecentBooksRunnable : public DBRunnable {
+
+public:
+ LoadRecentBooksRunnable(DBConnection &connection);
+ bool run();
+ void collectFileIds(std::vector<int> &fileIds);
+
+private:
+ std::vector<int> myFileIds;
+
+ shared_ptr<DBCommand> myLoadRecentBooks;
+};
+
+#endif /* __DBRUNNABLES_H__ */
diff --git a/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp b/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp
new file mode 100644
index 0000000..c0c9063
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+bool ClearBooksDBRunnable::run() {
+ shared_ptr<DBCommand> cmd;
+
+ cmd = SQLiteFactory::createCommand(BooksDBQuery::CLEAR_DATABASE, myConnection);
+ if (!cmd->execute()) {
+ return false;
+ }
+
+ cmd = SQLiteFactory::createCommand(BooksDBQuery::INIT_DATABASE, myConnection);
+ if (!cmd->execute()) {
+ return false;
+ }
+
+ cmd = SQLiteFactory::createCommand(BooksDBQuery::SECOND_INIT_DATABASE, myConnection);
+ if (!cmd->execute()) {
+ return false;
+ }
+
+ return true;
+}
+
+
diff --git a/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp
new file mode 100644
index 0000000..e9e2c14
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+DeleteBookRunnable::DeleteBookRunnable(DBConnection &connection) {
+ myFindFileId = new FindFileIdRunnable(connection);
+ myDeleteFile = SQLiteFactory::createCommand(BooksDBQuery::DELETE_FILE, connection, "@file_id", DBValue::DBINT);
+}
+
+bool DeleteBookRunnable::run() {
+ myFindFileId->setFileName(myFileName, true);
+ if (!myFindFileId->run()) {
+ return false;
+ }
+
+ (DBIntValue &) *myDeleteFile->parameter("@file_id").value() = myFindFileId->fileId();
+ return myDeleteFile->execute();
+}
+
diff --git a/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp
new file mode 100644
index 0000000..065a4c2
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+DeleteFileEntriesRunnable::DeleteFileEntriesRunnable(DBConnection &connection) {
+ myDeleteFileEntries = SQLiteFactory::createCommand(BooksDBQuery::DELETE_FILE_ENTRIES, connection, "@file_id", DBValue::DBINT);
+ myLoadFileEntryIds = SQLiteFactory::createCommand(BooksDBQuery::LOAD_FILE_ENTRY_IDS, connection, "@file_id", DBValue::DBINT);
+}
+
+bool DeleteFileEntriesRunnable::run() {
+ return doDelete(myFileId);
+}
+
+bool DeleteFileEntriesRunnable::doDelete(int fileId) {
+ (DBIntValue &) *myLoadFileEntryIds->parameter("@file_id").value() = fileId;
+ shared_ptr<DBDataReader> reader = myLoadFileEntryIds->executeReader();
+ if (reader.isNull()) {
+ return false;
+ }
+
+ std::vector<int> fileIds;
+ while (reader->next()) {
+ if (reader->type(0) != DBValue::DBINT /* file_id */ ) {
+ reader->close();
+ return false;
+ }
+ fileIds.push_back(reader->intValue(0));
+ }
+ reader->close();
+
+ if (fileIds.empty()) {
+ return true;
+ }
+ for (std::vector<int>::const_iterator it = fileIds.begin(); it != fileIds.end(); ++it) {
+ if (!doDelete(*it)) {
+ return false;
+ }
+ }
+ (DBIntValue &) *myDeleteFileEntries->parameter("@file_id").value() = fileId;
+ return myDeleteFileEntries->execute();
+}
+
diff --git a/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp b/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp
new file mode 100644
index 0000000..4461c4f
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 <ZLibrary.h>
+#include <ZLFile.h>
+
+#include "../DBRunnables.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+static const std::string FIND_FILE_ID =
+ "SELECT file_id FROM Files" \
+ " WHERE name = @name AND coalesce(parent_id, 0) = @parent_id;";
+
+static const std::string ADD_FILE =
+ "INSERT INTO Files (name, parent_id, size)" \
+ " VALUES(@name, nullif(@parent_id, 0), nullif(@size, 0));" \
+ " SELECT last_insert_rowid() AS file_id;";
+
+FindFileIdRunnable::FindFileIdRunnable(DBConnection &connection) {
+ myFindFileId = SQLiteFactory::createCommand(FIND_FILE_ID, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT);
+ myAddFile = SQLiteFactory::createCommand(ADD_FILE, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT, "@size", DBValue::DBINT);
+}
+
+bool FindFileIdRunnable::run() {
+ const std::string resolvedPath = ZLFile(myFileName).resolvedPath();
+ const std::string physPath = ZLFile(resolvedPath).physicalFilePath();
+ const std::string dirName = physPath.substr(0, physPath.rfind(ZLibrary::FileNameDelimiter));
+
+ DBTextValue &findName = (DBTextValue &) *myFindFileId->parameter("@name").value();
+ DBIntValue &findParent = (DBIntValue &) *myFindFileId->parameter("@parent_id").value();
+
+ DBTextValue &addName = (DBTextValue &) *myAddFile->parameter("@name").value();
+ DBIntValue &addParent = (DBIntValue &) *myAddFile->parameter("@parent_id").value();
+ ((DBIntValue &) *myAddFile->parameter("@size").value()) = 0;
+
+ std::size_t index = dirName.length() + 1;
+ findName = dirName;
+ findParent = 0;
+ while (true) {
+ shared_ptr<DBValue> physId = myFindFileId->executeScalar();
+ if (physId.isNull() || physId->type() != DBValue::DBINT || ((DBIntValue &) *physId).value() == 0) {
+ if (!myAdd) {
+ return false;
+ }
+ addName = findName.value();
+ addParent = findParent.value();
+ physId = myAddFile->executeScalar();
+ if (physId.isNull() || physId->type() != DBValue::DBINT || ((DBIntValue &) *physId).value() == 0) {
+ return false;
+ }
+ }
+ if (index == 0) {
+ myFileId = ((DBIntValue &) *physId).value();
+ return true;
+ }
+ std::size_t index2 = resolvedPath.find(BooksDBQuery::ArchiveEntryDelimiter, index);
+ findName = resolvedPath.substr(index, index2 - index);
+ index = index2 + 1;
+ findParent = ((DBIntValue &) *physId).value();
+ }
+}
+
+void FindFileIdRunnable::setFileName(const std::string &fileName, bool add) {
+ myFileName = fileName;
+ myAdd = add;
+ myFileId = 0;
+}
+
+int FindFileIdRunnable::fileId() const {
+ return myFileId;
+}
+
+SaveFileEntriesRunnable::SaveFileEntriesRunnable(DBConnection &connection) {
+ myAddFile = SQLiteFactory::createCommand(ADD_FILE, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT, "@size", DBValue::DBINT);
+
+ myFindFileId = new FindFileIdRunnable(connection);
+ myDeleteFileEntries = new DeleteFileEntriesRunnable(connection);
+}
+
+bool SaveFileEntriesRunnable::run() {
+ myFindFileId->setFileName(myFileName, true);
+ if (!myFindFileId->run()) {
+ return false;
+ }
+
+ myDeleteFileEntries->setFileId(myFindFileId->fileId());
+ if (!myDeleteFileEntries->run()) {
+ return false;
+ }
+
+ DBTextValue &addName = (DBTextValue &) *myAddFile->parameter("@name").value();
+ ((DBIntValue &) *myAddFile->parameter("@parent_id").value()) = myFindFileId->fileId();
+ ((DBIntValue &) *myAddFile->parameter("@size").value()) = 0;
+
+ for (std::vector<std::string>::const_iterator it = myEntries.begin(); it != myEntries.end(); ++it) {
+ const std::string &entry = (*it);
+ if (entry.empty()) {
+ continue;
+ }
+ addName = entry;
+ if (!myAddFile->execute()) {
+ return false;
+ }
+ }
+ return true;
+}
+
diff --git a/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp b/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp
new file mode 100644
index 0000000..b8a4b01
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+bool InitBooksDBRunnable::run() {
+ shared_ptr<DBCommand> cmd;
+ cmd = SQLiteFactory::createCommand(BooksDBQuery::INIT_DATABASE, myConnection);
+ if (!cmd->execute()) {
+ return false;
+ }
+
+ cmd = SQLiteFactory::createCommand(BooksDBQuery::SECOND_INIT_DATABASE, myConnection);
+ if (!cmd->execute()) {
+ return false;
+ }
+
+ return true;
+}
+
+
diff --git a/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp
new file mode 100644
index 0000000..3668b83
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+LoadFileEntriesRunnable::LoadFileEntriesRunnable(DBConnection &connection) {
+ myLoadFileEntries = SQLiteFactory::createCommand(BooksDBQuery::LOAD_FILE_ENTRIES, connection, "@file_id", DBValue::DBINT);
+
+ myFindFileId = new FindFileIdRunnable(connection);
+}
+
+bool LoadFileEntriesRunnable::run() {
+ DBCommand &cmd = *myLoadFileEntries;
+
+ myFindFileId->setFileName(myFileName);
+ if (!myFindFileId->run()) {
+ return false;
+ }
+ ((DBIntValue &) *cmd.parameter("@file_id").value()) = myFindFileId->fileId();
+
+ shared_ptr<DBDataReader> reader = cmd.executeReader();
+
+ if (reader.isNull()) {
+ return false;
+ }
+
+ myEntries.clear();
+
+ bool res = true;
+ while (reader->next()) {
+ if (reader->type(0) != DBValue::DBTEXT /* name */) {
+ res = false;
+ continue;
+ }
+ myEntries.push_back(
+ myFileName + BooksDBQuery::ArchiveEntryDelimiter +
+ reader->textValue(0, std::string())
+ );
+ }
+ reader->close();
+ return res;
+}
+
+void LoadFileEntriesRunnable::setFileName(const std::string &fileName) {
+ myFileName = fileName;
+}
+
+void LoadFileEntriesRunnable::collectEntries(std::vector<std::string> &entries) {
+ myEntries.swap(entries);
+ myEntries.clear();
+}
diff --git a/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp b/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp
new file mode 100644
index 0000000..06e6f79
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../../library/Author.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+LoadRecentBooksRunnable::LoadRecentBooksRunnable(DBConnection &connection) {
+ myLoadRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::LOAD_RECENT_BOOKS, connection);
+}
+
+bool LoadRecentBooksRunnable::run() {
+ shared_ptr<DBDataReader> reader = myLoadRecentBooks->executeReader();
+ if (reader.isNull()) {
+ return false;
+ }
+ myFileIds.clear();
+
+ bool res = true;
+ while (reader->next()) {
+ if (reader->type(0) != DBValue::DBINT /* file_id */) {
+ res = false;
+ continue;
+ }
+ const int fileId = reader->intValue(0);
+ myFileIds.push_back(fileId);
+ }
+ reader->close();
+ return res;
+}
+
+void LoadRecentBooksRunnable::collectFileIds(std::vector<int> &fileIds) {
+ myFileIds.swap(fileIds);
+ myFileIds.clear();
+}
diff --git a/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp
new file mode 100644
index 0000000..7336a74
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../../library/Book.h"
+#include "../../../library/Author.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+SaveAuthorsRunnable::SaveAuthorsRunnable(DBConnection &connection) {
+ mySetBookAuthor = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_AUTHOR, connection, "@author_id", DBValue::DBINT, "@book_id", DBValue::DBINT, "@author_index", DBValue::DBINT);
+ myTrimBookAuthors = SQLiteFactory::createCommand(BooksDBQuery::TRIM_BOOK_AUTHORS, connection, "@book_id", DBValue::DBINT, "@authors_number", DBValue::DBINT);
+ myFindAuthorId = SQLiteFactory::createCommand(BooksDBQuery::FIND_AUTHOR_ID, connection, "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT);
+ myAddAuthor = SQLiteFactory::createCommand(BooksDBQuery::ADD_AUTHOR, connection, "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT);
+}
+
+bool SaveAuthorsRunnable::run() {
+ if (myBook->bookId() == 0) {
+ return false;
+ }
+ const AuthorList &bookAuthors = myBook->authors(); // save link to vector
+
+ ((DBIntValue &) *mySetBookAuthor->parameter("@book_id").value()) = myBook->bookId();
+ DBIntValue &setAuthorId = (DBIntValue &) *mySetBookAuthor->parameter("@author_id").value();
+ DBIntValue &setAuthorIndex = (DBIntValue &) *mySetBookAuthor->parameter("@author_index").value();
+ DBTextValue &findAuthor = (DBTextValue &) *myFindAuthorId->parameter("@name").value();
+ DBTextValue &findSortKey = (DBTextValue &) *myFindAuthorId->parameter("@sort_key").value();
+ DBTextValue &addAuthor = (DBTextValue &) *myAddAuthor->parameter("@name").value();
+ DBTextValue &addSortKey = (DBTextValue &) *myAddAuthor->parameter("@sort_key").value();
+
+ int index = 0;
+ for (AuthorList::const_iterator it = bookAuthors.begin(); it != bookAuthors.end(); ++it) {
+ const Author &author = **it;
+ findAuthor.setValue( author.name() );
+ findSortKey.setValue( author.sortKey() );
+ shared_ptr<DBValue> tableAuthorId = myFindAuthorId->executeScalar();
+ if (tableAuthorId.isNull() || tableAuthorId->type() != DBValue::DBINT || ((DBIntValue &) *tableAuthorId).value() == 0) {
+ addAuthor.setValue( author.name() );
+ addSortKey.setValue( author.sortKey() );
+ tableAuthorId = myAddAuthor->executeScalar();
+ if (tableAuthorId.isNull() || tableAuthorId->type() != DBValue::DBINT || ((DBIntValue &) *tableAuthorId).value() == 0) {
+ return false;
+ }
+ }
+ setAuthorId = ((DBIntValue &) *tableAuthorId).value();
+ setAuthorIndex = ++index;
+ if (!mySetBookAuthor->execute()) {
+ return false;
+ }
+ }
+ ((DBIntValue &) *myTrimBookAuthors->parameter("@book_id").value()) = myBook->bookId();
+ ((DBIntValue &) *myTrimBookAuthors->parameter("@authors_number").value()) = index;
+ return myTrimBookAuthors->execute();
+}
+
+void SaveAuthorsRunnable::setBook(shared_ptr<Book> book) {
+ myBook = book;
+}
diff --git a/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp
new file mode 100644
index 0000000..7cf8dff
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../../library/Book.h"
+
+SaveBookRunnable::SaveBookRunnable(SaveTableBookRunnable &saveTableBook, SaveAuthorsRunnable &saveAuthors,
+ SaveSeriesRunnable &saveSeries, SaveTagsRunnable &saveTags) :
+ mySaveTableBook(saveTableBook),
+ mySaveAuthors(saveAuthors),
+ mySaveSeries(saveSeries),
+ mySaveTags(saveTags) {
+}
+
+bool SaveBookRunnable::run() {
+ return mySaveTableBook.run()
+ && mySaveAuthors.run()
+ && mySaveSeries.run()
+ && mySaveTags.run();
+}
+
+void SaveBookRunnable::setBook(shared_ptr<Book> book) {
+ mySaveTableBook.setBook(book);
+ mySaveAuthors.setBook(book);
+ mySaveTags.setBook(book);
+ mySaveSeries.setBook(book);
+}
diff --git a/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp
new file mode 100644
index 0000000..85d4a89
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+
+SaveBookStateStackRunnable::SaveBookStateStackRunnable(DBConnection &connection) {
+ myTrimBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::TRIM_BOOK_STATE_STACK, connection, "@book_id", DBValue::DBINT, "@stackSize", DBValue::DBINT);
+ mySetBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_STATE_STACK, connection, "@book_id", DBValue::DBINT, "@position", DBValue::DBINT, "@paragraph", DBValue::DBINT, "@word", DBValue::DBINT, "@char", DBValue::DBINT);
+}
+
+bool SaveBookStateStackRunnable::run() {
+ ((DBIntValue &) *myTrimBookStateStack->parameter("@book_id").value()) = myBookId;
+ ((DBIntValue &) *myTrimBookStateStack->parameter("@stackSize").value()) = myStack.size();
+
+ if (!myTrimBookStateStack->execute()) {
+ return false;
+ }
+
+ ((DBIntValue &) *mySetBookStateStack->parameter("@book_id").value()) = myBookId;
+
+ DBIntValue &savePosition = (DBIntValue &) *mySetBookStateStack->parameter("@position").value();
+ DBIntValue &saveParagraph = (DBIntValue &) *mySetBookStateStack->parameter("@paragraph").value();
+ DBIntValue &saveWord = (DBIntValue &) *mySetBookStateStack->parameter("@word").value();
+ DBIntValue &saveChar = (DBIntValue &) *mySetBookStateStack->parameter("@char").value();
+
+ for (std::size_t i = 0; i < myStack.size(); ++i) {
+ const ReadingState &pos = myStack[i];
+ savePosition = i + 1;
+ saveParagraph = pos.Paragraph;
+ saveWord = pos.Word;
+ saveChar = pos.Character;
+ if (!mySetBookStateStack->execute()) {
+ return false;
+ }
+ }
+ return true;
+}
+
diff --git a/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp
new file mode 100644
index 0000000..1c355ed
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../../library/Book.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+SaveRecentBooksRunnable::SaveRecentBooksRunnable(DBConnection &connection) {
+ myClearRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::CLEAR_RECENT_BOOKS, connection);
+ myInsertRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::INSERT_RECENT_BOOKS, connection, "@book_id", DBValue::DBINT);
+}
+
+bool SaveRecentBooksRunnable::run() {
+ if (!myClearRecentBooks->execute()) {
+ return false;
+ }
+ DBIntValue &insertBookId = (DBIntValue &) *myInsertRecentBooks->parameter("@book_id").value();
+ for (BookList::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) {
+ shared_ptr<Book> book = (*it);
+ if (book->bookId() == 0) {
+ return false;
+ }
+ insertBookId = book->bookId();
+ if (!myInsertRecentBooks->execute()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SaveRecentBooksRunnable::setBooks(const BookList &books) {
+ myBooks = books; // copy vector
+}
diff --git a/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp
new file mode 100644
index 0000000..e56777b
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../../library/Book.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+SaveSeriesRunnable::SaveSeriesRunnable(DBConnection &connection) {
+ mySetBookSeries = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOKSERIES, connection, "@book_id", DBValue::DBINT, "@series_id", DBValue::DBINT, "@book_index", DBValue::DBTEXT);
+ myDeleteBookSeries = SQLiteFactory::createCommand(BooksDBQuery::DELETE_BOOKSERIES, connection, "@book_id", DBValue::DBINT);
+ myFindSeriesId = SQLiteFactory::createCommand(BooksDBQuery::FIND_SERIES_ID, connection, "@name", DBValue::DBTEXT);
+ myAddSeries = SQLiteFactory::createCommand(BooksDBQuery::ADD_SERIES, connection, "@name", DBValue::DBTEXT);
+}
+
+bool SaveSeriesRunnable::run() {
+ if (myBook->bookId() == 0) {
+ return false;
+ }
+
+ if (myBook->seriesTitle().empty()) {
+ ((DBIntValue &) *myDeleteBookSeries->parameter("@book_id").value()) = myBook->bookId();
+ return myDeleteBookSeries->execute();
+ }
+
+ ((DBTextValue &) *myFindSeriesId->parameter("@name").value()) = myBook->seriesTitle();
+ shared_ptr<DBValue> tableSeriesId = myFindSeriesId->executeScalar();
+ if (tableSeriesId.isNull() || tableSeriesId->type() != DBValue::DBINT || ((DBIntValue &) *tableSeriesId).value() == 0) {
+ ((DBTextValue &) *myAddSeries->parameter("@name").value()) = myBook->seriesTitle();
+ tableSeriesId = myAddSeries->executeScalar();
+ if (tableSeriesId.isNull() || tableSeriesId->type() != DBValue::DBINT || ((DBIntValue &) *tableSeriesId).value() == 0) {
+ return false;
+ }
+ }
+ ((DBIntValue &) *mySetBookSeries->parameter("@book_id").value()) = myBook->bookId();
+ mySetBookSeries->parameter("@series_id").setValue( tableSeriesId );
+ ((DBTextValue &) *mySetBookSeries->parameter("@book_index").value()) = myBook->indexInSeries().value();
+ return mySetBookSeries->execute();
+}
+
+void SaveSeriesRunnable::setBook(shared_ptr<Book> book) {
+ myBook = book;
+}
diff --git a/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp
new file mode 100644
index 0000000..770963e
--- /dev/null
+++ b/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2009-2012 Geometer Plus <[email protected]>
+ *
+ * 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 "../DBRunnables.h"
+#include "../../../library/Book.h"
+#include "../../sqldb/implsqlite/SQLiteFactory.h"
+
+SaveTableBookRunnable::SaveTableBookRunnable(DBConnection &connection) {
+ myFindBookId = SQLiteFactory::createCommand(BooksDBQuery::FIND_BOOK_ID, connection, "@file_id", DBValue::DBINT);
+
+ myAddBook = SQLiteFactory::createCommand(BooksDBQuery::ADD_BOOK, connection, "@encoding", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@title", DBValue::DBTEXT, "@file_id", DBValue::DBINT);
+ myUpdateBook = SQLiteFactory::createCommand(BooksDBQuery::UPDATE_BOOK, connection, "@encoding", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@title", DBValue::DBTEXT, "@book_id", DBValue::DBINT);
+
+ myFindFileId = new FindFileIdRunnable(connection);
+}
+
+bool SaveTableBookRunnable::run() {
+ if (myBook->bookId() != 0) {
+ return updateTableBook(myBook);
+ }
+
+ myFindFileId->setFileName(myBook->file().path(), true);
+ if (!myFindFileId->run()) {
+ return false;
+ }
+ const int fileId = myFindFileId->fileId();
+
+ ((DBIntValue &) *myFindBookId->parameter("@file_id").value()) = fileId;
+ shared_ptr<DBValue> dbBookId = myFindBookId->executeScalar();
+
+ if (dbBookId.isNull() || dbBookId->type() != DBValue::DBINT || ((DBIntValue &) *dbBookId).value() == 0) {
+ return addTableBook(myBook, fileId);
+ } else {
+ myBook->setBookId( ((DBIntValue &) *dbBookId).value() );
+ return updateTableBook(myBook);
+ }
+}
+
+bool SaveTableBookRunnable::addTableBook(const shared_ptr<Book> book, int fileId) {
+
+ ((DBTextValue &) *myAddBook->parameter("@encoding").value()) = book->encoding();
+ ((DBTextValue &) *myAddBook->parameter("@language").value()) = book->language();
+ ((DBTextValue &) *myAddBook->parameter("@title").value()) = book->title();
+ ((DBIntValue &) *myAddBook->parameter("@file_id").value()) = fileId;
+ shared_ptr<DBValue> dbBookId = myAddBook->executeScalar();
+
+ if (dbBookId.isNull() || dbBookId->type() != DBValue::DBINT || ((DBIntValue &) *dbBookId).value() == 0) {
+ return false;
+ }
+ book->setBookId(((DBIntValue&)*dbBookId).value());
+ return true;
+}
+
+bool SaveTableBookRunnable::updateTableBook(const shared_ptr<Book> book) {
+ ((DBTextValue&)*myUpdateBook->parameter("@encoding").value()) = book->encoding();
+ ((DBTextValue&)*myUpdateBook->parameter("@language").value()) = book->language();
+ ((DBTextValue&)*myUpdateBook->parameter("@title").value()) = book->title();
+ ((DBIntValue&)*myUpdateBook->parameter("@book_id").value()) = book->bookId();
+ return myUpdateBook->execute();
+}
+
+void SaveTableBookRunnable::setBook(shared_ptr<Book> book) {
+ myBook = book;
+}