/* * Copyright (C) 2009-2012 Geometer Plus * * 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 #include #include #include #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::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 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 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 runnable = new ClearBooksDBRunnable(connection()); return executeAsTransaction(*runnable); } shared_ptr 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 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::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) { if (!isInitialized()) { return false; } mySaveBook->setBook(book); return executeAsTransaction(*mySaveBook); } bool BooksDB::saveAuthors(const shared_ptr book) { if (!isInitialized()) { return false; } mySaveAuthors->setBook(book); return executeAsTransaction(*mySaveAuthors); } bool BooksDB::saveSeries(const shared_ptr book) { if (!isInitialized()) { return false; } mySaveSeries->setBook(book); return executeAsTransaction(*mySaveSeries); } bool BooksDB::saveTags(const shared_ptr 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 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 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 &entries) { myLoadFileEntries->setFileName(fileName); if (!myLoadFileEntries->run()) { return false; } myLoadFileEntries->collectEntries(entries); return true; } bool BooksDB::saveFileEntries(const std::string &fileName, const std::vector &entries) { if (!isInitialized()) { return false; } mySaveFileEntries->setEntries(fileName, entries); return executeAsTransaction(*mySaveFileEntries); } bool BooksDB::loadRecentBooks(std::vector &fileNames) { std::vector fileIds; if (!myLoadRecentBooks->run()) { return false; } myLoadRecentBooks->collectFileIds(fileIds); for (std::vector::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 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 reader = myLoadBooks->executeReader(); books.clear(); std::map > 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::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 &stack) { if (book.bookId() == 0) { return false; } ((DBIntValue&)*myLoadBookStateStack->parameter("@book_id").value()) = book.bookId(); shared_ptr 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 &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 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 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 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 res = myCheckBookList->executeScalar(); if (res.isNull() || res->type() != DBValue::DBINT) { return false; } const int checkRes = ((DBIntValue&)*res).value(); return checkRes > 0; }