From 17b259df9cb6b28779d4881b2b6c805ee2e48eea Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Fri, 7 Jun 2024 23:30:05 +0900 Subject: Rename to tde-ebook-reader Signed-off-by: Michele Calgaro --- reader/src/blockTree/ReaderNode.cpp | 275 +++++ reader/src/blockTree/ReaderNode.h | 92 ++ reader/src/bookmodel/BookModel.cpp | 68 ++ reader/src/bookmodel/BookModel.h | 91 ++ reader/src/bookmodel/BookReader.cpp | 303 ++++++ reader/src/bookmodel/BookReader.h | 114 ++ reader/src/bookmodel/FBHyperlinkType.h | 30 + reader/src/bookmodel/FBTextKind.h | 70 ++ reader/src/database/booksdb/BooksDB.cpp | 503 +++++++++ reader/src/database/booksdb/BooksDB.h | 174 ++++ reader/src/database/booksdb/BooksDBQuery.cpp | 327 ++++++ reader/src/database/booksdb/BooksDBQuery.h | 99 ++ reader/src/database/booksdb/BooksDBUtil.cpp | 184 ++++ reader/src/database/booksdb/BooksDBUtil.h | 61 ++ reader/src/database/booksdb/BooksDB_BookAuthor.cpp | 76 ++ reader/src/database/booksdb/BooksDB_BookSeries.cpp | 83 ++ reader/src/database/booksdb/BooksDB_BookTag.cpp | 246 +++++ reader/src/database/booksdb/DBRunnables.h | 311 ++++++ .../booksdb/runnables/ClearBooksDBRunnable.cpp | 46 + .../booksdb/runnables/DeleteBookRunnable.cpp | 37 + .../runnables/DeleteFileEntriesRunnable.cpp | 60 ++ .../booksdb/runnables/FindFileIdRunnable.cpp | 123 +++ .../booksdb/runnables/InitBooksDBRunnable.cpp | 40 + .../booksdb/runnables/LoadFileEntriesRunnable.cpp | 68 ++ .../booksdb/runnables/LoadRecentBooksRunnable.cpp | 51 + .../booksdb/runnables/SaveAuthorsRunnable.cpp | 75 ++ .../booksdb/runnables/SaveBookRunnable.cpp | 43 + .../runnables/SaveBookStateStackRunnable.cpp | 56 + .../booksdb/runnables/SaveRecentBooksRunnable.cpp | 49 + .../booksdb/runnables/SaveSeriesRunnable.cpp | 58 ++ .../booksdb/runnables/SaveTableBookRunnable.cpp | 80 ++ reader/src/database/networkdb/DBRunnables.h | 81 ++ reader/src/database/networkdb/NetworkDB.cpp | 152 +++ reader/src/database/networkdb/NetworkDB.h | 76 ++ reader/src/database/networkdb/NetworkDBQuery.cpp | 103 ++ reader/src/database/networkdb/NetworkDBQuery.h | 48 + .../networkdb/runnables/ClearNetworkDBRunnable.cpp | 39 + .../networkdb/runnables/InitNetworkDBRunnable.cpp | 30 + .../runnables/SaveNetworkLinkRunnable.cpp | 138 +++ reader/src/database/sqldb/DBCommand.cpp | 59 ++ reader/src/database/sqldb/DBCommand.h | 69 ++ reader/src/database/sqldb/DBCommandParameter.cpp | 63 ++ reader/src/database/sqldb/DBCommandParameter.h | 81 ++ reader/src/database/sqldb/DBConnection.cpp | 28 + reader/src/database/sqldb/DBConnection.h | 40 + reader/src/database/sqldb/DBDataReader.cpp | 26 + reader/src/database/sqldb/DBDataReader.h | 58 ++ reader/src/database/sqldb/DBIntValue.cpp | 37 + reader/src/database/sqldb/DBNullValue.cpp | 34 + reader/src/database/sqldb/DBRealValue.cpp | 37 + reader/src/database/sqldb/DBRunnable.h | 32 + reader/src/database/sqldb/DBTextValue.cpp | 48 + reader/src/database/sqldb/DBValue.cpp | 42 + reader/src/database/sqldb/DBValues.h | 145 +++ reader/src/database/sqldb/DataBase.cpp | 31 + reader/src/database/sqldb/DataBase.h | 55 + .../database/sqldb/implsqlite/SQLiteCommand.cpp | 366 +++++++ .../src/database/sqldb/implsqlite/SQLiteCommand.h | 117 +++ .../database/sqldb/implsqlite/SQLiteConnection.cpp | 86 ++ .../database/sqldb/implsqlite/SQLiteConnection.h | 81 ++ .../database/sqldb/implsqlite/SQLiteDataBase.cpp | 107 ++ .../src/database/sqldb/implsqlite/SQLiteDataBase.h | 82 ++ .../database/sqldb/implsqlite/SQLiteDataReader.cpp | 133 +++ .../database/sqldb/implsqlite/SQLiteDataReader.h | 64 ++ .../database/sqldb/implsqlite/SQLiteFactory.cpp | 113 ++ .../src/database/sqldb/implsqlite/SQLiteFactory.h | 61 ++ reader/src/encodingOption/EncodingOptionEntry.cpp | 122 +++ reader/src/encodingOption/EncodingOptionEntry.h | 74 ++ reader/src/external/ProgramCollection.cpp | 188 ++++ reader/src/external/ProgramCollection.h | 90 ++ reader/src/formats/EncodedTextReader.cpp | 29 + reader/src/formats/EncodedTextReader.h | 37 + reader/src/formats/FormatPlugin.cpp | 106 ++ reader/src/formats/FormatPlugin.h | 99 ++ reader/src/formats/PluginCollection.cpp | 89 ++ reader/src/formats/chm/BitStream.cpp | 44 + reader/src/formats/chm/BitStream.h | 111 ++ reader/src/formats/chm/CHMFile.cpp | 490 +++++++++ reader/src/formats/chm/CHMFile.h | 128 +++ reader/src/formats/chm/CHMFileImage.cpp | 33 + reader/src/formats/chm/CHMFileImage.h | 40 + reader/src/formats/chm/CHMPlugin.cpp | 252 +++++ reader/src/formats/chm/CHMPlugin.h | 41 + reader/src/formats/chm/CHMReferenceCollection.cpp | 91 ++ reader/src/formats/chm/CHMReferenceCollection.h | 50 + reader/src/formats/chm/E8Decoder.cpp | 61 ++ reader/src/formats/chm/HHCReader.cpp | 107 ++ reader/src/formats/chm/HHCReader.h | 57 + reader/src/formats/chm/HHCReferenceCollector.cpp | 62 ++ reader/src/formats/chm/HHCReferenceCollector.h | 45 + reader/src/formats/chm/HtmlSectionReader.cpp | 128 +++ reader/src/formats/chm/HtmlSectionReader.h | 50 + reader/src/formats/chm/HuffmanDecoder.cpp | 60 ++ reader/src/formats/chm/HuffmanDecoder.h | 53 + reader/src/formats/chm/LZXDecompressor.cpp | 287 ++++++ reader/src/formats/chm/LZXDecompressor.h | 88 ++ reader/src/formats/css/StyleSheetParser.cpp | 244 +++++ reader/src/formats/css/StyleSheetParser.h | 84 ++ reader/src/formats/css/StyleSheetTable.cpp | 267 +++++ reader/src/formats/css/StyleSheetTable.h | 76 ++ reader/src/formats/doc/DocBookReader.cpp | 377 +++++++ reader/src/formats/doc/DocBookReader.h | 103 ++ reader/src/formats/doc/DocFloatImageReader.cpp | 384 +++++++ reader/src/formats/doc/DocFloatImageReader.h | 107 ++ reader/src/formats/doc/DocInlineImageReader.cpp | 148 +++ reader/src/formats/doc/DocInlineImageReader.h | 37 + reader/src/formats/doc/DocMetaInfoReader.cpp | 38 + reader/src/formats/doc/DocMetaInfoReader.h | 46 + reader/src/formats/doc/DocPlugin.cpp | 71 ++ reader/src/formats/doc/DocPlugin.h | 39 + reader/src/formats/doc/DocStreams.cpp | 202 ++++ reader/src/formats/doc/DocStreams.h | 73 ++ reader/src/formats/doc/OleMainStream.cpp | 1085 ++++++++++++++++++++ reader/src/formats/doc/OleMainStream.h | 223 ++++ reader/src/formats/doc/OleStorage.cpp | 304 ++++++ reader/src/formats/doc/OleStorage.h | 92 ++ reader/src/formats/doc/OleStream.cpp | 221 ++++ reader/src/formats/doc/OleStream.h | 58 ++ reader/src/formats/doc/OleStreamParser.cpp | 210 ++++ reader/src/formats/doc/OleStreamParser.h | 101 ++ reader/src/formats/doc/OleStreamReader.cpp | 86 ++ reader/src/formats/doc/OleStreamReader.h | 46 + reader/src/formats/doc/OleUtil.cpp | 58 ++ reader/src/formats/doc/OleUtil.h | 32 + reader/src/formats/docbook/DocBookBookReader.cpp | 111 ++ reader/src/formats/docbook/DocBookBookReader.h | 45 + .../formats/docbook/DocBookDescriptionReader.cpp | 137 +++ .../src/formats/docbook/DocBookDescriptionReader.h | 56 + reader/src/formats/docbook/DocBookPlugin.cpp | 43 + reader/src/formats/docbook/DocBookPlugin.h | 41 + reader/src/formats/docbook/DocBookReader.cpp | 71 ++ reader/src/formats/docbook/DocBookReader.h | 95 ++ reader/src/formats/dummy/DummyBookReader.cpp | 42 + reader/src/formats/dummy/DummyBookReader.h | 44 + reader/src/formats/dummy/DummyMetaInfoReader.cpp | 40 + reader/src/formats/dummy/DummyMetaInfoReader.h | 46 + reader/src/formats/dummy/DummyPlugin.cpp | 57 + reader/src/formats/dummy/DummyPlugin.h | 38 + reader/src/formats/dummy/createPlugin.sh | 12 + reader/src/formats/fb2/FB2BookReader.cpp | 336 ++++++ reader/src/formats/fb2/FB2BookReader.h | 61 ++ reader/src/formats/fb2/FB2CoverReader.cpp | 92 ++ reader/src/formats/fb2/FB2CoverReader.h | 49 + reader/src/formats/fb2/FB2MetaInfoReader.cpp | 206 ++++ reader/src/formats/fb2/FB2MetaInfoReader.h | 60 ++ reader/src/formats/fb2/FB2Plugin.cpp | 48 + reader/src/formats/fb2/FB2Plugin.h | 42 + reader/src/formats/fb2/FB2Reader.cpp | 89 ++ reader/src/formats/fb2/FB2Reader.h | 94 ++ reader/src/formats/fb2/FB2TagManager.cpp | 124 +++ reader/src/formats/fb2/FB2TagManager.h | 45 + reader/src/formats/html/HtmlBookReader.cpp | 583 +++++++++++ reader/src/formats/html/HtmlBookReader.h | 101 ++ reader/src/formats/html/HtmlDescriptionReader.cpp | 82 ++ reader/src/formats/html/HtmlDescriptionReader.h | 48 + reader/src/formats/html/HtmlEntityCollection.cpp | 71 ++ reader/src/formats/html/HtmlEntityCollection.h | 38 + reader/src/formats/html/HtmlPlugin.cpp | 83 ++ reader/src/formats/html/HtmlPlugin.h | 42 + reader/src/formats/html/HtmlReader.cpp | 373 +++++++ reader/src/formats/html/HtmlReader.h | 92 ++ reader/src/formats/html/HtmlReaderStream.cpp | 128 +++ reader/src/formats/html/HtmlReaderStream.h | 48 + reader/src/formats/html/HtmlTagActions.h | 158 +++ reader/src/formats/oeb/NCXReader.cpp | 131 +++ reader/src/formats/oeb/NCXReader.h | 69 ++ reader/src/formats/oeb/OEBBookReader.cpp | 273 +++++ reader/src/formats/oeb/OEBBookReader.h | 70 ++ reader/src/formats/oeb/OEBCoverReader.cpp | 136 +++ reader/src/formats/oeb/OEBCoverReader.h | 56 + reader/src/formats/oeb/OEBMetaInfoReader.cpp | 194 ++++ reader/src/formats/oeb/OEBMetaInfoReader.h | 63 ++ reader/src/formats/oeb/OEBPlugin.cpp | 149 +++ reader/src/formats/oeb/OEBPlugin.h | 40 + reader/src/formats/oeb/OEBTextStream.cpp | 101 ++ reader/src/formats/oeb/OEBTextStream.h | 43 + reader/src/formats/oeb/XHTMLImageFinder.cpp | 54 + reader/src/formats/oeb/XHTMLImageFinder.h | 43 + reader/src/formats/openreader/ORBookReader.cpp | 185 ++++ reader/src/formats/openreader/ORBookReader.h | 77 ++ .../src/formats/openreader/ORDescriptionReader.cpp | 88 ++ .../src/formats/openreader/ORDescriptionReader.h | 53 + reader/src/formats/openreader/OpenReaderPlugin.cpp | 52 + reader/src/formats/openreader/OpenReaderPlugin.h | 36 + reader/src/formats/pdb/BitReader.cpp | 57 + reader/src/formats/pdb/BitReader.h | 39 + reader/src/formats/pdb/DocDecompressor.cpp | 103 ++ reader/src/formats/pdb/DocDecompressor.h | 36 + reader/src/formats/pdb/EReaderPlugin.cpp | 125 +++ reader/src/formats/pdb/EReaderStream.cpp | 289 ++++++ reader/src/formats/pdb/EReaderStream.h | 88 ++ reader/src/formats/pdb/HtmlMetainfoReader.cpp | 89 ++ reader/src/formats/pdb/HtmlMetainfoReader.h | 60 ++ reader/src/formats/pdb/HuffDecompressor.cpp | 192 ++++ reader/src/formats/pdb/HuffDecompressor.h | 63 ++ .../src/formats/pdb/MobipocketHtmlBookReader.cpp | 356 +++++++ reader/src/formats/pdb/MobipocketHtmlBookReader.h | 89 ++ reader/src/formats/pdb/MobipocketPlugin.cpp | 229 +++++ reader/src/formats/pdb/PalmDocLikePlugin.cpp | 40 + reader/src/formats/pdb/PalmDocLikeStream.cpp | 78 ++ reader/src/formats/pdb/PalmDocLikeStream.h | 58 ++ reader/src/formats/pdb/PalmDocPlugin.cpp | 54 + reader/src/formats/pdb/PalmDocStream.cpp | 209 ++++ reader/src/formats/pdb/PalmDocStream.h | 50 + reader/src/formats/pdb/PdbPlugin.cpp | 69 ++ reader/src/formats/pdb/PdbPlugin.h | 119 +++ reader/src/formats/pdb/PdbReader.cpp | 108 ++ reader/src/formats/pdb/PdbReader.h | 82 ++ reader/src/formats/pdb/PdbStream.cpp | 109 ++ reader/src/formats/pdb/PdbStream.h | 72 ++ reader/src/formats/pdb/PluckerBookReader.cpp | 528 ++++++++++ reader/src/formats/pdb/PluckerBookReader.h | 89 ++ reader/src/formats/pdb/PluckerImages.cpp | 80 ++ reader/src/formats/pdb/PluckerImages.h | 79 ++ reader/src/formats/pdb/PluckerPlugin.cpp | 48 + reader/src/formats/pdb/PluckerTextStream.cpp | 159 +++ reader/src/formats/pdb/PluckerTextStream.h | 48 + reader/src/formats/pdb/PmlBookReader.cpp | 227 ++++ reader/src/formats/pdb/PmlBookReader.h | 73 ++ reader/src/formats/pdb/PmlReader.cpp | 407 ++++++++ reader/src/formats/pdb/PmlReader.h | 117 +++ reader/src/formats/pdb/SimplePdbPlugin.cpp | 75 ++ reader/src/formats/pdb/ZTXTPlugin.cpp | 43 + reader/src/formats/pdb/ZTXTStream.cpp | 77 ++ reader/src/formats/pdb/ZTXTStream.h | 45 + reader/src/formats/pdf/PdfBookReader.cpp | 261 +++++ reader/src/formats/pdf/PdfBookReader.h | 52 + reader/src/formats/pdf/PdfDescriptionReader.cpp | 29 + reader/src/formats/pdf/PdfDescriptionReader.h | 40 + reader/src/formats/pdf/PdfObject.cpp | 450 ++++++++ reader/src/formats/pdf/PdfObject.h | 201 ++++ reader/src/formats/pdf/PdfPlugin.cpp | 42 + reader/src/formats/pdf/PdfPlugin.h | 41 + reader/src/formats/pdf/StringStream.cpp | 55 + reader/src/formats/pdf/StringStream.h | 44 + reader/src/formats/rtf/RtfBookReader.cpp | 232 +++++ reader/src/formats/rtf/RtfBookReader.h | 71 ++ reader/src/formats/rtf/RtfDescriptionReader.cpp | 100 ++ reader/src/formats/rtf/RtfDescriptionReader.h | 55 + reader/src/formats/rtf/RtfPlugin.cpp | 63 ++ reader/src/formats/rtf/RtfPlugin.h | 35 + reader/src/formats/rtf/RtfReader.cpp | 470 +++++++++ reader/src/formats/rtf/RtfReader.h | 209 ++++ reader/src/formats/rtf/RtfReaderStream.cpp | 175 ++++ reader/src/formats/rtf/RtfReaderStream.h | 50 + reader/src/formats/tcr/PPLBookReader.cpp | 129 +++ reader/src/formats/tcr/PPLBookReader.h | 51 + reader/src/formats/tcr/TcrPlugin.cpp | 82 ++ reader/src/formats/tcr/TcrPlugin.h | 43 + reader/src/formats/tcr/TcrStream.cpp | 125 +++ reader/src/formats/tcr/TcrStream.h | 47 + reader/src/formats/txt/PlainTextFormat.cpp | 253 +++++ reader/src/formats/txt/PlainTextFormat.h | 112 ++ reader/src/formats/txt/TxtBookReader.cpp | 124 +++ reader/src/formats/txt/TxtBookReader.h | 59 ++ reader/src/formats/txt/TxtPlugin.cpp | 79 ++ reader/src/formats/txt/TxtPlugin.h | 37 + reader/src/formats/txt/TxtReader.cpp | 200 ++++ reader/src/formats/txt/TxtReader.h | 56 + reader/src/formats/util/EntityFilesCollector.cpp | 62 ++ reader/src/formats/util/EntityFilesCollector.h | 42 + reader/src/formats/util/MergedStream.cpp | 72 ++ reader/src/formats/util/MergedStream.h | 45 + reader/src/formats/util/MiscUtil.cpp | 91 ++ reader/src/formats/util/MiscUtil.h | 39 + reader/src/formats/util/TextFormatDetector.cpp | 77 ++ reader/src/formats/util/TextFormatDetector.h | 35 + reader/src/formats/util/XMLTextStream.cpp | 124 +++ reader/src/formats/util/XMLTextStream.h | 52 + reader/src/formats/xhtml/XHTMLReader.cpp | 715 +++++++++++++ reader/src/formats/xhtml/XHTMLReader.h | 113 ++ reader/src/library/Author.cpp | 67 ++ reader/src/library/Author.h | 71 ++ reader/src/library/Book.cpp | 280 +++++ reader/src/library/Book.h | 142 +++ reader/src/library/Comparators.cpp | 106 ++ reader/src/library/Library.cpp | 439 ++++++++ reader/src/library/Library.h | 126 +++ reader/src/library/Lists.h | 39 + reader/src/library/Number.cpp | 64 ++ reader/src/library/Number.h | 45 + reader/src/library/Tag.cpp | 139 +++ reader/src/library/Tag.h | 98 ++ reader/src/libraryActions/AuthorInfoDialog.cpp | 164 +++ reader/src/libraryActions/AuthorInfoDialog.h | 64 ++ reader/src/libraryActions/BooksUtil.cpp | 88 ++ reader/src/libraryActions/BooksUtil.h | 39 + reader/src/libraryActions/LibraryAuthorActions.cpp | 41 + reader/src/libraryActions/LibraryAuthorActions.h | 41 + reader/src/libraryActions/LibraryBookActions.cpp | 132 +++ reader/src/libraryActions/LibraryBookActions.h | 67 ++ reader/src/libraryActions/LibraryTagActions.cpp | 159 +++ reader/src/libraryActions/LibraryTagActions.h | 80 ++ reader/src/libraryTree/AuthorNode.cpp | 60 ++ reader/src/libraryTree/BookNode.cpp | 116 +++ reader/src/libraryTree/LibraryByAuthorView.cpp | 173 ++++ reader/src/libraryTree/LibraryByTagView.cpp | 97 ++ reader/src/libraryTree/LibraryNodes.h | 120 +++ reader/src/libraryTree/LibraryView.cpp | 86 ++ reader/src/libraryTree/LibraryView.h | 86 ++ reader/src/libraryTree/SeriesNode.cpp | 61 ++ reader/src/libraryTree/TagNode.cpp | 78 ++ reader/src/migration/BookInfo.cpp | 66 ++ reader/src/migration/BookInfo.h | 49 + reader/src/migration/FB2MigrationReader.cpp | 106 ++ reader/src/migration/FB2MigrationReader.h | 57 + reader/src/migration/HtmlDCTagsReader.cpp | 61 ++ reader/src/migration/HtmlDCTagsReader.h | 47 + reader/src/migration/Migration.cpp | 77 ++ reader/src/migration/Migration.h | 119 +++ reader/src/migration/Migration_0_10_4.cpp | 42 + reader/src/migration/Migration_0_11_0.cpp | 544 ++++++++++ reader/src/migration/Migration_0_8_11.cpp | 122 +++ reader/src/migration/Migration_0_8_13.cpp | 43 + reader/src/migration/Migration_0_8_16.cpp | 83 ++ reader/src/migration/Migration_0_99_0.cpp | 91 ++ reader/src/migration/Migration_0_99_1.cpp | 34 + reader/src/migration/OEBMigrationReader.cpp | 81 ++ reader/src/migration/OEBMigrationReader.h | 54 + reader/src/migration/migrate.cpp | 45 + reader/src/migration/migrate.h | 37 + reader/src/network/BookReference.cpp | 60 ++ reader/src/network/BookReference.h | 87 ++ reader/src/network/NetworkBookCollection.cpp | 71 ++ reader/src/network/NetworkBookCollection.h | 60 ++ reader/src/network/NetworkBookItem.cpp | 191 ++++ reader/src/network/NetworkCatalogItem.cpp | 91 ++ reader/src/network/NetworkComparators.cpp | 120 +++ reader/src/network/NetworkComparators.h | 57 + reader/src/network/NetworkErrors.cpp | 70 ++ reader/src/network/NetworkErrors.h | 60 ++ reader/src/network/NetworkItem.cpp | 37 + reader/src/network/NetworkItems.h | 202 ++++ reader/src/network/NetworkLink.cpp | 146 +++ reader/src/network/NetworkLink.h | 109 ++ reader/src/network/NetworkLinkCollection.cpp | 561 ++++++++++ reader/src/network/NetworkLinkCollection.h | 101 ++ reader/src/network/NetworkOperationData.cpp | 37 + reader/src/network/NetworkOperationData.h | 45 + reader/src/network/SearchResult.cpp | 36 + reader/src/network/SearchResult.h | 49 + reader/src/network/UserList.cpp | 85 ++ reader/src/network/UserList.h | 44 + reader/src/network/atom/ATOMConstructs.cpp | 339 ++++++ reader/src/network/atom/ATOMConstructs.h | 135 +++ reader/src/network/atom/ATOMContainers.cpp | 49 + reader/src/network/atom/ATOMContainers.h | 142 +++ reader/src/network/atom/ATOMMetadata.cpp | 202 ++++ reader/src/network/atom/ATOMMetadata.h | 221 ++++ .../NetworkAuthenticationManager.cpp | 80 ++ .../authentication/NetworkAuthenticationManager.h | 88 ++ .../litres/LitResAuthenticationDataParser.cpp | 158 +++ .../litres/LitResAuthenticationDataParser.h | 107 ++ .../litres/LitResAuthenticationManager.cpp | 472 +++++++++ .../litres/LitResAuthenticationManager.h | 93 ++ reader/src/network/litres/LitResAuthorsItem.cpp | 111 ++ reader/src/network/litres/LitResAuthorsItem.h | 51 + reader/src/network/litres/LitResAuthorsParser.cpp | 185 ++++ reader/src/network/litres/LitResAuthorsParser.h | 83 ++ reader/src/network/litres/LitResBookItem.cpp | 70 ++ reader/src/network/litres/LitResBookItem.h | 53 + reader/src/network/litres/LitResBooksFeedItem.cpp | 128 +++ reader/src/network/litres/LitResBooksFeedItem.h | 60 ++ .../src/network/litres/LitResBooksFeedParser.cpp | 433 ++++++++ reader/src/network/litres/LitResBooksFeedParser.h | 89 ++ reader/src/network/litres/LitResBookshelfItem.cpp | 111 ++ reader/src/network/litres/LitResBookshelfItem.h | 51 + reader/src/network/litres/LitResByGenresItem.cpp | 71 ++ reader/src/network/litres/LitResByGenresItem.h | 47 + reader/src/network/litres/LitResGenre.cpp | 206 ++++ reader/src/network/litres/LitResGenre.h | 66 ++ reader/src/network/litres/LitResGenresParser.cpp | 81 ++ reader/src/network/litres/LitResGenresParser.h | 56 + .../network/litres/LitResRecommendationsItem.cpp | 51 + .../src/network/litres/LitResRecommendationsItem.h | 40 + reader/src/network/litres/LitResUtil.cpp | 178 ++++ reader/src/network/litres/LitResUtil.h | 54 + reader/src/network/litres/SortedCatalogItem.cpp | 49 + reader/src/network/litres/SortedCatalogItem.h | 100 ++ reader/src/network/opds/NetworkOPDSFeedReader.cpp | 198 ++++ reader/src/network/opds/NetworkOPDSFeedReader.h | 60 ++ reader/src/network/opds/OPDSBookItem.cpp | 310 ++++++ reader/src/network/opds/OPDSBookItem.h | 88 ++ reader/src/network/opds/OPDSCatalogItem.cpp | 81 ++ reader/src/network/opds/OPDSCatalogItem.h | 51 + reader/src/network/opds/OPDSFeedReader.h | 40 + reader/src/network/opds/OPDSLink.cpp | 216 ++++ reader/src/network/opds/OPDSLink.h | 112 ++ reader/src/network/opds/OPDSLink_AdvancedSearch.h | 72 ++ .../network/opds/OPDSLink_GenericFeedReader.cpp | 134 +++ .../src/network/opds/OPDSLink_GenericFeedReader.h | 61 ++ .../src/network/opds/OPDSLink_GenericXMLParser.cpp | 109 ++ .../src/network/opds/OPDSLink_GenericXMLParser.h | 35 + reader/src/network/opds/OPDSMetadata.cpp | 89 ++ reader/src/network/opds/OPDSMetadata.h | 139 +++ reader/src/network/opds/OPDSXMLParser.cpp | 554 ++++++++++ reader/src/network/opds/OPDSXMLParser.h | 79 ++ reader/src/network/opds/OpenSearchXMLReader.cpp | 49 + reader/src/network/opds/OpenSearchXMLReader.h | 42 + reader/src/network/opds/URLRewritingRule.cpp | 77 ++ reader/src/network/opds/URLRewritingRule.h | 50 + reader/src/network/tree/NetworkAuthorTree.cpp | 53 + reader/src/network/tree/NetworkBookTree.cpp | 262 +++++ reader/src/network/tree/NetworkCatalogRootTree.cpp | 265 +++++ reader/src/network/tree/NetworkCatalogTree.cpp | 292 ++++++ reader/src/network/tree/NetworkCatalogUtil.cpp | 92 ++ reader/src/network/tree/NetworkCatalogUtil.h | 45 + reader/src/network/tree/NetworkLibrary.cpp | 101 ++ reader/src/network/tree/NetworkLibrary.h | 57 + reader/src/network/tree/NetworkSearcher.cpp | 179 ++++ reader/src/network/tree/NetworkSearcher.h | 59 ++ reader/src/network/tree/NetworkSeriesTree.cpp | 72 ++ reader/src/network/tree/NetworkTree.cpp | 31 + reader/src/network/tree/NetworkTreeFactory.cpp | 123 +++ reader/src/network/tree/NetworkTreeFactory.h | 42 + reader/src/network/tree/NetworkTreeNodes.h | 275 +++++ reader/src/network/tree/RootTree.cpp | 42 + reader/src/network/tree/SearchCatalogTree.cpp | 41 + reader/src/networkActions/AuthenticationDialog.cpp | 122 +++ reader/src/networkActions/AuthenticationDialog.h | 50 + .../networkActions/AuthenticationDialogManager.cpp | 192 ++++ .../networkActions/AuthenticationDialogManager.h | 37 + reader/src/networkActions/NetworkActions.cpp | 357 +++++++ reader/src/networkActions/NetworkActions.h | 103 ++ .../networkActions/NetworkOperationRunnable.cpp | 187 ++++ .../src/networkActions/NetworkOperationRunnable.h | 184 ++++ .../src/networkActions/PasswordRecoveryDialog.cpp | 95 ++ reader/src/networkActions/PasswordRecoveryDialog.h | 46 + reader/src/networkActions/RegisterUserDialog.cpp | 146 +++ reader/src/networkActions/RegisterUserDialog.h | 50 + reader/src/options/FBCategoryKey.cpp | 27 + reader/src/options/FBCategoryKey.h | 36 + reader/src/options/FBOptions.cpp | 64 ++ reader/src/options/FBOptions.h | 65 ++ reader/src/options/FBTextStyle.cpp | 107 ++ reader/src/options/FBTextStyle.h | 71 ++ reader/src/optionsDialog/AbstractOptionsDialog.cpp | 48 + reader/src/optionsDialog/AbstractOptionsDialog.h | 50 + reader/src/optionsDialog/IntegrationTab.cpp | 159 +++ .../src/optionsDialog/bookInfo/BookInfoDialog.cpp | 564 ++++++++++ reader/src/optionsDialog/bookInfo/BookInfoDialog.h | 80 ++ .../optionsDialog/library/LibraryOptionsDialog.cpp | 39 + .../optionsDialog/library/LibraryOptionsDialog.h | 32 + .../lookAndFeel/FormatOptionsPage.cpp | 112 ++ .../optionsDialog/lookAndFeel/FormatOptionsPage.h | 33 + .../lookAndFeel/LookAndFeelOptionsDialog.cpp | 78 ++ .../lookAndFeel/LookAndFeelOptionsDialog.h | 38 + .../src/optionsDialog/lookAndFeel/OptionsPage.cpp | 52 + reader/src/optionsDialog/lookAndFeel/OptionsPage.h | 76 ++ .../optionsDialog/lookAndFeel/StyleOptionsPage.cpp | 127 +++ .../optionsDialog/lookAndFeel/StyleOptionsPage.h | 34 + .../optionsDialog/network/NetworkOptionsDialog.cpp | 104 ++ .../optionsDialog/network/NetworkOptionsDialog.h | 32 + reader/src/optionsDialog/reading/IndicatorTab.cpp | 171 +++ .../src/optionsDialog/reading/KeyBindingsTab.cpp | 285 +++++ .../optionsDialog/reading/ReadingOptionsDialog.cpp | 125 +++ .../optionsDialog/reading/ReadingOptionsDialog.h | 36 + .../optionsDialog/system/SystemOptionsDialog.cpp | 86 ++ .../src/optionsDialog/system/SystemOptionsDialog.h | 32 + reader/src/reader/AddBookAction.cpp | 58 ++ reader/src/reader/BookTextView.cpp | 419 ++++++++ reader/src/reader/BookTextView.h | 100 ++ reader/src/reader/BooksOrderAction.cpp | 28 + reader/src/reader/ContentsView.cpp | 108 ++ reader/src/reader/ContentsView.h | 40 + reader/src/reader/FootnoteView.h | 33 + reader/src/reader/PreferencesPopupData.cpp | 73 ++ reader/src/reader/PreferencesPopupData.h | 46 + reader/src/reader/Reader.cpp | 558 ++++++++++ reader/src/reader/Reader.h | 207 ++++ reader/src/reader/ReaderActionCode.cpp | 70 ++ reader/src/reader/ReaderActions.cpp | 493 +++++++++ reader/src/reader/ReaderActions.h | 409 ++++++++ reader/src/reader/ReadingState.h | 41 + reader/src/reader/RecentBooksPopupData.cpp | 64 ++ reader/src/reader/RecentBooksPopupData.h | 41 + reader/src/reader/ScrollingAction.cpp | 101 ++ reader/src/reader/ScrollingAction.h | 88 ++ reader/src/reader/SearchActions.cpp | 155 +++ reader/src/reader/SearchOnNetworkAction.cpp | 177 ++++ reader/src/reader/TimeUpdater.cpp | 40 + reader/src/reader/TimeUpdater.h | 37 + reader/src/reader/View.cpp | 316 ++++++ reader/src/reader/View.h | 123 +++ reader/src/reader/main.cpp | 31 + reader/src/tree/FBTree.cpp | 142 +++ reader/src/tree/FBTree.h | 64 ++ 487 files changed, 55986 insertions(+) create mode 100644 reader/src/blockTree/ReaderNode.cpp create mode 100644 reader/src/blockTree/ReaderNode.h create mode 100644 reader/src/bookmodel/BookModel.cpp create mode 100644 reader/src/bookmodel/BookModel.h create mode 100644 reader/src/bookmodel/BookReader.cpp create mode 100644 reader/src/bookmodel/BookReader.h create mode 100644 reader/src/bookmodel/FBHyperlinkType.h create mode 100644 reader/src/bookmodel/FBTextKind.h create mode 100644 reader/src/database/booksdb/BooksDB.cpp create mode 100644 reader/src/database/booksdb/BooksDB.h create mode 100644 reader/src/database/booksdb/BooksDBQuery.cpp create mode 100644 reader/src/database/booksdb/BooksDBQuery.h create mode 100644 reader/src/database/booksdb/BooksDBUtil.cpp create mode 100644 reader/src/database/booksdb/BooksDBUtil.h create mode 100644 reader/src/database/booksdb/BooksDB_BookAuthor.cpp create mode 100644 reader/src/database/booksdb/BooksDB_BookSeries.cpp create mode 100644 reader/src/database/booksdb/BooksDB_BookTag.cpp create mode 100644 reader/src/database/booksdb/DBRunnables.h create mode 100644 reader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/DeleteBookRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/FindFileIdRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/SaveBookRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp create mode 100644 reader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp create mode 100644 reader/src/database/networkdb/DBRunnables.h create mode 100644 reader/src/database/networkdb/NetworkDB.cpp create mode 100644 reader/src/database/networkdb/NetworkDB.h create mode 100644 reader/src/database/networkdb/NetworkDBQuery.cpp create mode 100644 reader/src/database/networkdb/NetworkDBQuery.h create mode 100644 reader/src/database/networkdb/runnables/ClearNetworkDBRunnable.cpp create mode 100644 reader/src/database/networkdb/runnables/InitNetworkDBRunnable.cpp create mode 100644 reader/src/database/networkdb/runnables/SaveNetworkLinkRunnable.cpp create mode 100644 reader/src/database/sqldb/DBCommand.cpp create mode 100644 reader/src/database/sqldb/DBCommand.h create mode 100644 reader/src/database/sqldb/DBCommandParameter.cpp create mode 100644 reader/src/database/sqldb/DBCommandParameter.h create mode 100644 reader/src/database/sqldb/DBConnection.cpp create mode 100644 reader/src/database/sqldb/DBConnection.h create mode 100644 reader/src/database/sqldb/DBDataReader.cpp create mode 100644 reader/src/database/sqldb/DBDataReader.h create mode 100644 reader/src/database/sqldb/DBIntValue.cpp create mode 100644 reader/src/database/sqldb/DBNullValue.cpp create mode 100644 reader/src/database/sqldb/DBRealValue.cpp create mode 100644 reader/src/database/sqldb/DBRunnable.h create mode 100644 reader/src/database/sqldb/DBTextValue.cpp create mode 100644 reader/src/database/sqldb/DBValue.cpp create mode 100644 reader/src/database/sqldb/DBValues.h create mode 100644 reader/src/database/sqldb/DataBase.cpp create mode 100644 reader/src/database/sqldb/DataBase.h create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteCommand.cpp create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteCommand.h create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteConnection.cpp create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteConnection.h create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteDataBase.cpp create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteDataBase.h create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteDataReader.cpp create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteDataReader.h create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteFactory.cpp create mode 100644 reader/src/database/sqldb/implsqlite/SQLiteFactory.h create mode 100644 reader/src/encodingOption/EncodingOptionEntry.cpp create mode 100644 reader/src/encodingOption/EncodingOptionEntry.h create mode 100644 reader/src/external/ProgramCollection.cpp create mode 100644 reader/src/external/ProgramCollection.h create mode 100644 reader/src/formats/EncodedTextReader.cpp create mode 100644 reader/src/formats/EncodedTextReader.h create mode 100644 reader/src/formats/FormatPlugin.cpp create mode 100644 reader/src/formats/FormatPlugin.h create mode 100644 reader/src/formats/PluginCollection.cpp create mode 100644 reader/src/formats/chm/BitStream.cpp create mode 100644 reader/src/formats/chm/BitStream.h create mode 100644 reader/src/formats/chm/CHMFile.cpp create mode 100644 reader/src/formats/chm/CHMFile.h create mode 100644 reader/src/formats/chm/CHMFileImage.cpp create mode 100644 reader/src/formats/chm/CHMFileImage.h create mode 100644 reader/src/formats/chm/CHMPlugin.cpp create mode 100644 reader/src/formats/chm/CHMPlugin.h create mode 100644 reader/src/formats/chm/CHMReferenceCollection.cpp create mode 100644 reader/src/formats/chm/CHMReferenceCollection.h create mode 100644 reader/src/formats/chm/E8Decoder.cpp create mode 100644 reader/src/formats/chm/HHCReader.cpp create mode 100644 reader/src/formats/chm/HHCReader.h create mode 100644 reader/src/formats/chm/HHCReferenceCollector.cpp create mode 100644 reader/src/formats/chm/HHCReferenceCollector.h create mode 100644 reader/src/formats/chm/HtmlSectionReader.cpp create mode 100644 reader/src/formats/chm/HtmlSectionReader.h create mode 100644 reader/src/formats/chm/HuffmanDecoder.cpp create mode 100644 reader/src/formats/chm/HuffmanDecoder.h create mode 100644 reader/src/formats/chm/LZXDecompressor.cpp create mode 100644 reader/src/formats/chm/LZXDecompressor.h create mode 100644 reader/src/formats/css/StyleSheetParser.cpp create mode 100644 reader/src/formats/css/StyleSheetParser.h create mode 100644 reader/src/formats/css/StyleSheetTable.cpp create mode 100644 reader/src/formats/css/StyleSheetTable.h create mode 100644 reader/src/formats/doc/DocBookReader.cpp create mode 100644 reader/src/formats/doc/DocBookReader.h create mode 100644 reader/src/formats/doc/DocFloatImageReader.cpp create mode 100644 reader/src/formats/doc/DocFloatImageReader.h create mode 100644 reader/src/formats/doc/DocInlineImageReader.cpp create mode 100644 reader/src/formats/doc/DocInlineImageReader.h create mode 100644 reader/src/formats/doc/DocMetaInfoReader.cpp create mode 100644 reader/src/formats/doc/DocMetaInfoReader.h create mode 100644 reader/src/formats/doc/DocPlugin.cpp create mode 100644 reader/src/formats/doc/DocPlugin.h create mode 100644 reader/src/formats/doc/DocStreams.cpp create mode 100644 reader/src/formats/doc/DocStreams.h create mode 100644 reader/src/formats/doc/OleMainStream.cpp create mode 100644 reader/src/formats/doc/OleMainStream.h create mode 100644 reader/src/formats/doc/OleStorage.cpp create mode 100644 reader/src/formats/doc/OleStorage.h create mode 100644 reader/src/formats/doc/OleStream.cpp create mode 100644 reader/src/formats/doc/OleStream.h create mode 100644 reader/src/formats/doc/OleStreamParser.cpp create mode 100644 reader/src/formats/doc/OleStreamParser.h create mode 100644 reader/src/formats/doc/OleStreamReader.cpp create mode 100644 reader/src/formats/doc/OleStreamReader.h create mode 100644 reader/src/formats/doc/OleUtil.cpp create mode 100644 reader/src/formats/doc/OleUtil.h create mode 100644 reader/src/formats/docbook/DocBookBookReader.cpp create mode 100644 reader/src/formats/docbook/DocBookBookReader.h create mode 100644 reader/src/formats/docbook/DocBookDescriptionReader.cpp create mode 100644 reader/src/formats/docbook/DocBookDescriptionReader.h create mode 100644 reader/src/formats/docbook/DocBookPlugin.cpp create mode 100644 reader/src/formats/docbook/DocBookPlugin.h create mode 100644 reader/src/formats/docbook/DocBookReader.cpp create mode 100644 reader/src/formats/docbook/DocBookReader.h create mode 100644 reader/src/formats/dummy/DummyBookReader.cpp create mode 100644 reader/src/formats/dummy/DummyBookReader.h create mode 100644 reader/src/formats/dummy/DummyMetaInfoReader.cpp create mode 100644 reader/src/formats/dummy/DummyMetaInfoReader.h create mode 100644 reader/src/formats/dummy/DummyPlugin.cpp create mode 100644 reader/src/formats/dummy/DummyPlugin.h create mode 100755 reader/src/formats/dummy/createPlugin.sh create mode 100644 reader/src/formats/fb2/FB2BookReader.cpp create mode 100644 reader/src/formats/fb2/FB2BookReader.h create mode 100644 reader/src/formats/fb2/FB2CoverReader.cpp create mode 100644 reader/src/formats/fb2/FB2CoverReader.h create mode 100644 reader/src/formats/fb2/FB2MetaInfoReader.cpp create mode 100644 reader/src/formats/fb2/FB2MetaInfoReader.h create mode 100644 reader/src/formats/fb2/FB2Plugin.cpp create mode 100644 reader/src/formats/fb2/FB2Plugin.h create mode 100644 reader/src/formats/fb2/FB2Reader.cpp create mode 100644 reader/src/formats/fb2/FB2Reader.h create mode 100644 reader/src/formats/fb2/FB2TagManager.cpp create mode 100644 reader/src/formats/fb2/FB2TagManager.h create mode 100644 reader/src/formats/html/HtmlBookReader.cpp create mode 100644 reader/src/formats/html/HtmlBookReader.h create mode 100644 reader/src/formats/html/HtmlDescriptionReader.cpp create mode 100644 reader/src/formats/html/HtmlDescriptionReader.h create mode 100644 reader/src/formats/html/HtmlEntityCollection.cpp create mode 100644 reader/src/formats/html/HtmlEntityCollection.h create mode 100644 reader/src/formats/html/HtmlPlugin.cpp create mode 100644 reader/src/formats/html/HtmlPlugin.h create mode 100644 reader/src/formats/html/HtmlReader.cpp create mode 100644 reader/src/formats/html/HtmlReader.h create mode 100644 reader/src/formats/html/HtmlReaderStream.cpp create mode 100644 reader/src/formats/html/HtmlReaderStream.h create mode 100644 reader/src/formats/html/HtmlTagActions.h create mode 100644 reader/src/formats/oeb/NCXReader.cpp create mode 100644 reader/src/formats/oeb/NCXReader.h create mode 100644 reader/src/formats/oeb/OEBBookReader.cpp create mode 100644 reader/src/formats/oeb/OEBBookReader.h create mode 100644 reader/src/formats/oeb/OEBCoverReader.cpp create mode 100644 reader/src/formats/oeb/OEBCoverReader.h create mode 100644 reader/src/formats/oeb/OEBMetaInfoReader.cpp create mode 100644 reader/src/formats/oeb/OEBMetaInfoReader.h create mode 100644 reader/src/formats/oeb/OEBPlugin.cpp create mode 100644 reader/src/formats/oeb/OEBPlugin.h create mode 100644 reader/src/formats/oeb/OEBTextStream.cpp create mode 100644 reader/src/formats/oeb/OEBTextStream.h create mode 100644 reader/src/formats/oeb/XHTMLImageFinder.cpp create mode 100644 reader/src/formats/oeb/XHTMLImageFinder.h create mode 100644 reader/src/formats/openreader/ORBookReader.cpp create mode 100644 reader/src/formats/openreader/ORBookReader.h create mode 100644 reader/src/formats/openreader/ORDescriptionReader.cpp create mode 100644 reader/src/formats/openreader/ORDescriptionReader.h create mode 100644 reader/src/formats/openreader/OpenReaderPlugin.cpp create mode 100644 reader/src/formats/openreader/OpenReaderPlugin.h create mode 100644 reader/src/formats/pdb/BitReader.cpp create mode 100644 reader/src/formats/pdb/BitReader.h create mode 100644 reader/src/formats/pdb/DocDecompressor.cpp create mode 100644 reader/src/formats/pdb/DocDecompressor.h create mode 100644 reader/src/formats/pdb/EReaderPlugin.cpp create mode 100644 reader/src/formats/pdb/EReaderStream.cpp create mode 100644 reader/src/formats/pdb/EReaderStream.h create mode 100644 reader/src/formats/pdb/HtmlMetainfoReader.cpp create mode 100644 reader/src/formats/pdb/HtmlMetainfoReader.h create mode 100644 reader/src/formats/pdb/HuffDecompressor.cpp create mode 100644 reader/src/formats/pdb/HuffDecompressor.h create mode 100644 reader/src/formats/pdb/MobipocketHtmlBookReader.cpp create mode 100644 reader/src/formats/pdb/MobipocketHtmlBookReader.h create mode 100644 reader/src/formats/pdb/MobipocketPlugin.cpp create mode 100644 reader/src/formats/pdb/PalmDocLikePlugin.cpp create mode 100644 reader/src/formats/pdb/PalmDocLikeStream.cpp create mode 100644 reader/src/formats/pdb/PalmDocLikeStream.h create mode 100644 reader/src/formats/pdb/PalmDocPlugin.cpp create mode 100644 reader/src/formats/pdb/PalmDocStream.cpp create mode 100644 reader/src/formats/pdb/PalmDocStream.h create mode 100644 reader/src/formats/pdb/PdbPlugin.cpp create mode 100644 reader/src/formats/pdb/PdbPlugin.h create mode 100644 reader/src/formats/pdb/PdbReader.cpp create mode 100644 reader/src/formats/pdb/PdbReader.h create mode 100644 reader/src/formats/pdb/PdbStream.cpp create mode 100644 reader/src/formats/pdb/PdbStream.h create mode 100644 reader/src/formats/pdb/PluckerBookReader.cpp create mode 100644 reader/src/formats/pdb/PluckerBookReader.h create mode 100644 reader/src/formats/pdb/PluckerImages.cpp create mode 100644 reader/src/formats/pdb/PluckerImages.h create mode 100644 reader/src/formats/pdb/PluckerPlugin.cpp create mode 100644 reader/src/formats/pdb/PluckerTextStream.cpp create mode 100644 reader/src/formats/pdb/PluckerTextStream.h create mode 100644 reader/src/formats/pdb/PmlBookReader.cpp create mode 100644 reader/src/formats/pdb/PmlBookReader.h create mode 100644 reader/src/formats/pdb/PmlReader.cpp create mode 100644 reader/src/formats/pdb/PmlReader.h create mode 100644 reader/src/formats/pdb/SimplePdbPlugin.cpp create mode 100644 reader/src/formats/pdb/ZTXTPlugin.cpp create mode 100644 reader/src/formats/pdb/ZTXTStream.cpp create mode 100644 reader/src/formats/pdb/ZTXTStream.h create mode 100644 reader/src/formats/pdf/PdfBookReader.cpp create mode 100644 reader/src/formats/pdf/PdfBookReader.h create mode 100644 reader/src/formats/pdf/PdfDescriptionReader.cpp create mode 100644 reader/src/formats/pdf/PdfDescriptionReader.h create mode 100644 reader/src/formats/pdf/PdfObject.cpp create mode 100644 reader/src/formats/pdf/PdfObject.h create mode 100644 reader/src/formats/pdf/PdfPlugin.cpp create mode 100644 reader/src/formats/pdf/PdfPlugin.h create mode 100644 reader/src/formats/pdf/StringStream.cpp create mode 100644 reader/src/formats/pdf/StringStream.h create mode 100644 reader/src/formats/rtf/RtfBookReader.cpp create mode 100644 reader/src/formats/rtf/RtfBookReader.h create mode 100644 reader/src/formats/rtf/RtfDescriptionReader.cpp create mode 100644 reader/src/formats/rtf/RtfDescriptionReader.h create mode 100644 reader/src/formats/rtf/RtfPlugin.cpp create mode 100644 reader/src/formats/rtf/RtfPlugin.h create mode 100644 reader/src/formats/rtf/RtfReader.cpp create mode 100644 reader/src/formats/rtf/RtfReader.h create mode 100644 reader/src/formats/rtf/RtfReaderStream.cpp create mode 100644 reader/src/formats/rtf/RtfReaderStream.h create mode 100644 reader/src/formats/tcr/PPLBookReader.cpp create mode 100644 reader/src/formats/tcr/PPLBookReader.h create mode 100644 reader/src/formats/tcr/TcrPlugin.cpp create mode 100644 reader/src/formats/tcr/TcrPlugin.h create mode 100644 reader/src/formats/tcr/TcrStream.cpp create mode 100644 reader/src/formats/tcr/TcrStream.h create mode 100644 reader/src/formats/txt/PlainTextFormat.cpp create mode 100644 reader/src/formats/txt/PlainTextFormat.h create mode 100644 reader/src/formats/txt/TxtBookReader.cpp create mode 100644 reader/src/formats/txt/TxtBookReader.h create mode 100644 reader/src/formats/txt/TxtPlugin.cpp create mode 100644 reader/src/formats/txt/TxtPlugin.h create mode 100644 reader/src/formats/txt/TxtReader.cpp create mode 100644 reader/src/formats/txt/TxtReader.h create mode 100644 reader/src/formats/util/EntityFilesCollector.cpp create mode 100644 reader/src/formats/util/EntityFilesCollector.h create mode 100644 reader/src/formats/util/MergedStream.cpp create mode 100644 reader/src/formats/util/MergedStream.h create mode 100644 reader/src/formats/util/MiscUtil.cpp create mode 100644 reader/src/formats/util/MiscUtil.h create mode 100644 reader/src/formats/util/TextFormatDetector.cpp create mode 100644 reader/src/formats/util/TextFormatDetector.h create mode 100644 reader/src/formats/util/XMLTextStream.cpp create mode 100644 reader/src/formats/util/XMLTextStream.h create mode 100644 reader/src/formats/xhtml/XHTMLReader.cpp create mode 100644 reader/src/formats/xhtml/XHTMLReader.h create mode 100644 reader/src/library/Author.cpp create mode 100644 reader/src/library/Author.h create mode 100644 reader/src/library/Book.cpp create mode 100644 reader/src/library/Book.h create mode 100644 reader/src/library/Comparators.cpp create mode 100644 reader/src/library/Library.cpp create mode 100644 reader/src/library/Library.h create mode 100644 reader/src/library/Lists.h create mode 100644 reader/src/library/Number.cpp create mode 100644 reader/src/library/Number.h create mode 100644 reader/src/library/Tag.cpp create mode 100644 reader/src/library/Tag.h create mode 100644 reader/src/libraryActions/AuthorInfoDialog.cpp create mode 100644 reader/src/libraryActions/AuthorInfoDialog.h create mode 100644 reader/src/libraryActions/BooksUtil.cpp create mode 100644 reader/src/libraryActions/BooksUtil.h create mode 100644 reader/src/libraryActions/LibraryAuthorActions.cpp create mode 100644 reader/src/libraryActions/LibraryAuthorActions.h create mode 100644 reader/src/libraryActions/LibraryBookActions.cpp create mode 100644 reader/src/libraryActions/LibraryBookActions.h create mode 100644 reader/src/libraryActions/LibraryTagActions.cpp create mode 100644 reader/src/libraryActions/LibraryTagActions.h create mode 100644 reader/src/libraryTree/AuthorNode.cpp create mode 100644 reader/src/libraryTree/BookNode.cpp create mode 100644 reader/src/libraryTree/LibraryByAuthorView.cpp create mode 100644 reader/src/libraryTree/LibraryByTagView.cpp create mode 100644 reader/src/libraryTree/LibraryNodes.h create mode 100644 reader/src/libraryTree/LibraryView.cpp create mode 100644 reader/src/libraryTree/LibraryView.h create mode 100644 reader/src/libraryTree/SeriesNode.cpp create mode 100644 reader/src/libraryTree/TagNode.cpp create mode 100644 reader/src/migration/BookInfo.cpp create mode 100644 reader/src/migration/BookInfo.h create mode 100644 reader/src/migration/FB2MigrationReader.cpp create mode 100644 reader/src/migration/FB2MigrationReader.h create mode 100644 reader/src/migration/HtmlDCTagsReader.cpp create mode 100644 reader/src/migration/HtmlDCTagsReader.h create mode 100644 reader/src/migration/Migration.cpp create mode 100644 reader/src/migration/Migration.h create mode 100644 reader/src/migration/Migration_0_10_4.cpp create mode 100644 reader/src/migration/Migration_0_11_0.cpp create mode 100644 reader/src/migration/Migration_0_8_11.cpp create mode 100644 reader/src/migration/Migration_0_8_13.cpp create mode 100644 reader/src/migration/Migration_0_8_16.cpp create mode 100644 reader/src/migration/Migration_0_99_0.cpp create mode 100644 reader/src/migration/Migration_0_99_1.cpp create mode 100644 reader/src/migration/OEBMigrationReader.cpp create mode 100644 reader/src/migration/OEBMigrationReader.h create mode 100644 reader/src/migration/migrate.cpp create mode 100644 reader/src/migration/migrate.h create mode 100644 reader/src/network/BookReference.cpp create mode 100644 reader/src/network/BookReference.h create mode 100644 reader/src/network/NetworkBookCollection.cpp create mode 100644 reader/src/network/NetworkBookCollection.h create mode 100644 reader/src/network/NetworkBookItem.cpp create mode 100644 reader/src/network/NetworkCatalogItem.cpp create mode 100644 reader/src/network/NetworkComparators.cpp create mode 100644 reader/src/network/NetworkComparators.h create mode 100644 reader/src/network/NetworkErrors.cpp create mode 100644 reader/src/network/NetworkErrors.h create mode 100644 reader/src/network/NetworkItem.cpp create mode 100644 reader/src/network/NetworkItems.h create mode 100644 reader/src/network/NetworkLink.cpp create mode 100644 reader/src/network/NetworkLink.h create mode 100644 reader/src/network/NetworkLinkCollection.cpp create mode 100644 reader/src/network/NetworkLinkCollection.h create mode 100644 reader/src/network/NetworkOperationData.cpp create mode 100644 reader/src/network/NetworkOperationData.h create mode 100644 reader/src/network/SearchResult.cpp create mode 100644 reader/src/network/SearchResult.h create mode 100644 reader/src/network/UserList.cpp create mode 100644 reader/src/network/UserList.h create mode 100644 reader/src/network/atom/ATOMConstructs.cpp create mode 100644 reader/src/network/atom/ATOMConstructs.h create mode 100644 reader/src/network/atom/ATOMContainers.cpp create mode 100644 reader/src/network/atom/ATOMContainers.h create mode 100644 reader/src/network/atom/ATOMMetadata.cpp create mode 100644 reader/src/network/atom/ATOMMetadata.h create mode 100644 reader/src/network/authentication/NetworkAuthenticationManager.cpp create mode 100644 reader/src/network/authentication/NetworkAuthenticationManager.h create mode 100644 reader/src/network/authentication/litres/LitResAuthenticationDataParser.cpp create mode 100644 reader/src/network/authentication/litres/LitResAuthenticationDataParser.h create mode 100644 reader/src/network/authentication/litres/LitResAuthenticationManager.cpp create mode 100644 reader/src/network/authentication/litres/LitResAuthenticationManager.h create mode 100644 reader/src/network/litres/LitResAuthorsItem.cpp create mode 100644 reader/src/network/litres/LitResAuthorsItem.h create mode 100644 reader/src/network/litres/LitResAuthorsParser.cpp create mode 100644 reader/src/network/litres/LitResAuthorsParser.h create mode 100644 reader/src/network/litres/LitResBookItem.cpp create mode 100644 reader/src/network/litres/LitResBookItem.h create mode 100644 reader/src/network/litres/LitResBooksFeedItem.cpp create mode 100644 reader/src/network/litres/LitResBooksFeedItem.h create mode 100644 reader/src/network/litres/LitResBooksFeedParser.cpp create mode 100644 reader/src/network/litres/LitResBooksFeedParser.h create mode 100644 reader/src/network/litres/LitResBookshelfItem.cpp create mode 100644 reader/src/network/litres/LitResBookshelfItem.h create mode 100644 reader/src/network/litres/LitResByGenresItem.cpp create mode 100644 reader/src/network/litres/LitResByGenresItem.h create mode 100644 reader/src/network/litres/LitResGenre.cpp create mode 100644 reader/src/network/litres/LitResGenre.h create mode 100644 reader/src/network/litres/LitResGenresParser.cpp create mode 100644 reader/src/network/litres/LitResGenresParser.h create mode 100644 reader/src/network/litres/LitResRecommendationsItem.cpp create mode 100644 reader/src/network/litres/LitResRecommendationsItem.h create mode 100644 reader/src/network/litres/LitResUtil.cpp create mode 100644 reader/src/network/litres/LitResUtil.h create mode 100644 reader/src/network/litres/SortedCatalogItem.cpp create mode 100644 reader/src/network/litres/SortedCatalogItem.h create mode 100644 reader/src/network/opds/NetworkOPDSFeedReader.cpp create mode 100644 reader/src/network/opds/NetworkOPDSFeedReader.h create mode 100644 reader/src/network/opds/OPDSBookItem.cpp create mode 100644 reader/src/network/opds/OPDSBookItem.h create mode 100644 reader/src/network/opds/OPDSCatalogItem.cpp create mode 100644 reader/src/network/opds/OPDSCatalogItem.h create mode 100644 reader/src/network/opds/OPDSFeedReader.h create mode 100644 reader/src/network/opds/OPDSLink.cpp create mode 100644 reader/src/network/opds/OPDSLink.h create mode 100644 reader/src/network/opds/OPDSLink_AdvancedSearch.h create mode 100644 reader/src/network/opds/OPDSLink_GenericFeedReader.cpp create mode 100644 reader/src/network/opds/OPDSLink_GenericFeedReader.h create mode 100644 reader/src/network/opds/OPDSLink_GenericXMLParser.cpp create mode 100644 reader/src/network/opds/OPDSLink_GenericXMLParser.h create mode 100644 reader/src/network/opds/OPDSMetadata.cpp create mode 100644 reader/src/network/opds/OPDSMetadata.h create mode 100644 reader/src/network/opds/OPDSXMLParser.cpp create mode 100644 reader/src/network/opds/OPDSXMLParser.h create mode 100644 reader/src/network/opds/OpenSearchXMLReader.cpp create mode 100644 reader/src/network/opds/OpenSearchXMLReader.h create mode 100644 reader/src/network/opds/URLRewritingRule.cpp create mode 100644 reader/src/network/opds/URLRewritingRule.h create mode 100644 reader/src/network/tree/NetworkAuthorTree.cpp create mode 100644 reader/src/network/tree/NetworkBookTree.cpp create mode 100644 reader/src/network/tree/NetworkCatalogRootTree.cpp create mode 100644 reader/src/network/tree/NetworkCatalogTree.cpp create mode 100644 reader/src/network/tree/NetworkCatalogUtil.cpp create mode 100644 reader/src/network/tree/NetworkCatalogUtil.h create mode 100644 reader/src/network/tree/NetworkLibrary.cpp create mode 100644 reader/src/network/tree/NetworkLibrary.h create mode 100644 reader/src/network/tree/NetworkSearcher.cpp create mode 100644 reader/src/network/tree/NetworkSearcher.h create mode 100644 reader/src/network/tree/NetworkSeriesTree.cpp create mode 100644 reader/src/network/tree/NetworkTree.cpp create mode 100644 reader/src/network/tree/NetworkTreeFactory.cpp create mode 100644 reader/src/network/tree/NetworkTreeFactory.h create mode 100644 reader/src/network/tree/NetworkTreeNodes.h create mode 100644 reader/src/network/tree/RootTree.cpp create mode 100644 reader/src/network/tree/SearchCatalogTree.cpp create mode 100644 reader/src/networkActions/AuthenticationDialog.cpp create mode 100644 reader/src/networkActions/AuthenticationDialog.h create mode 100644 reader/src/networkActions/AuthenticationDialogManager.cpp create mode 100644 reader/src/networkActions/AuthenticationDialogManager.h create mode 100644 reader/src/networkActions/NetworkActions.cpp create mode 100644 reader/src/networkActions/NetworkActions.h create mode 100644 reader/src/networkActions/NetworkOperationRunnable.cpp create mode 100644 reader/src/networkActions/NetworkOperationRunnable.h create mode 100644 reader/src/networkActions/PasswordRecoveryDialog.cpp create mode 100644 reader/src/networkActions/PasswordRecoveryDialog.h create mode 100644 reader/src/networkActions/RegisterUserDialog.cpp create mode 100644 reader/src/networkActions/RegisterUserDialog.h create mode 100644 reader/src/options/FBCategoryKey.cpp create mode 100644 reader/src/options/FBCategoryKey.h create mode 100644 reader/src/options/FBOptions.cpp create mode 100644 reader/src/options/FBOptions.h create mode 100644 reader/src/options/FBTextStyle.cpp create mode 100644 reader/src/options/FBTextStyle.h create mode 100644 reader/src/optionsDialog/AbstractOptionsDialog.cpp create mode 100644 reader/src/optionsDialog/AbstractOptionsDialog.h create mode 100644 reader/src/optionsDialog/IntegrationTab.cpp create mode 100644 reader/src/optionsDialog/bookInfo/BookInfoDialog.cpp create mode 100644 reader/src/optionsDialog/bookInfo/BookInfoDialog.h create mode 100644 reader/src/optionsDialog/library/LibraryOptionsDialog.cpp create mode 100644 reader/src/optionsDialog/library/LibraryOptionsDialog.h create mode 100644 reader/src/optionsDialog/lookAndFeel/FormatOptionsPage.cpp create mode 100644 reader/src/optionsDialog/lookAndFeel/FormatOptionsPage.h create mode 100644 reader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.cpp create mode 100644 reader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.h create mode 100644 reader/src/optionsDialog/lookAndFeel/OptionsPage.cpp create mode 100644 reader/src/optionsDialog/lookAndFeel/OptionsPage.h create mode 100644 reader/src/optionsDialog/lookAndFeel/StyleOptionsPage.cpp create mode 100644 reader/src/optionsDialog/lookAndFeel/StyleOptionsPage.h create mode 100644 reader/src/optionsDialog/network/NetworkOptionsDialog.cpp create mode 100644 reader/src/optionsDialog/network/NetworkOptionsDialog.h create mode 100644 reader/src/optionsDialog/reading/IndicatorTab.cpp create mode 100644 reader/src/optionsDialog/reading/KeyBindingsTab.cpp create mode 100644 reader/src/optionsDialog/reading/ReadingOptionsDialog.cpp create mode 100644 reader/src/optionsDialog/reading/ReadingOptionsDialog.h create mode 100644 reader/src/optionsDialog/system/SystemOptionsDialog.cpp create mode 100644 reader/src/optionsDialog/system/SystemOptionsDialog.h create mode 100644 reader/src/reader/AddBookAction.cpp create mode 100644 reader/src/reader/BookTextView.cpp create mode 100644 reader/src/reader/BookTextView.h create mode 100644 reader/src/reader/BooksOrderAction.cpp create mode 100644 reader/src/reader/ContentsView.cpp create mode 100644 reader/src/reader/ContentsView.h create mode 100644 reader/src/reader/FootnoteView.h create mode 100644 reader/src/reader/PreferencesPopupData.cpp create mode 100644 reader/src/reader/PreferencesPopupData.h create mode 100644 reader/src/reader/Reader.cpp create mode 100644 reader/src/reader/Reader.h create mode 100644 reader/src/reader/ReaderActionCode.cpp create mode 100644 reader/src/reader/ReaderActions.cpp create mode 100644 reader/src/reader/ReaderActions.h create mode 100644 reader/src/reader/ReadingState.h create mode 100644 reader/src/reader/RecentBooksPopupData.cpp create mode 100644 reader/src/reader/RecentBooksPopupData.h create mode 100644 reader/src/reader/ScrollingAction.cpp create mode 100644 reader/src/reader/ScrollingAction.h create mode 100644 reader/src/reader/SearchActions.cpp create mode 100644 reader/src/reader/SearchOnNetworkAction.cpp create mode 100644 reader/src/reader/TimeUpdater.cpp create mode 100644 reader/src/reader/TimeUpdater.h create mode 100644 reader/src/reader/View.cpp create mode 100644 reader/src/reader/View.h create mode 100644 reader/src/reader/main.cpp create mode 100644 reader/src/tree/FBTree.cpp create mode 100644 reader/src/tree/FBTree.h (limited to 'reader/src') diff --git a/reader/src/blockTree/ReaderNode.cpp b/reader/src/blockTree/ReaderNode.cpp new file mode 100644 index 0000000..cdca140 --- /dev/null +++ b/reader/src/blockTree/ReaderNode.cpp @@ -0,0 +1,275 @@ +/* + * 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 "ReaderNode.h" + +#include "../reader/Reader.h" +#include "../options/FBOptions.h" +#include "../options/FBTextStyle.h" + +const ZLTypeId ReaderNode::TYPE_ID(ZLBlockTreeNode::TYPE_ID); + +class ReaderNode::ExpandTreeAction : public ZLRunnableWithKey { + +public: + ExpandTreeAction(ReaderNode &node); + void run(); + ZLResourceKey key() const; + +private: + ReaderNode &myNode; +}; + +std::map > ReaderNode::ourDefaultCovers; + +ReaderNode::ReaderNode(ZLBlockTreeNode *parent, std::size_t atPosition) : ZLBlockTreeNode(parent, atPosition), myCoverImageIsStored(false), myIsInitialized(false) { +} + +void ReaderNode::init() { +} + +bool ReaderNode::highlighted() const { + return false; +} + +ReaderNode::~ReaderNode() { +} + +const ZLTypeId &ReaderNode::typeId() const { + return TYPE_ID; +} + +shared_ptr ReaderNode::coverImage() const { + if (!myCoverImageIsStored) { + myCoverImageIsStored = true; + myStoredCoverImage = extractCoverImage(); + } + return myStoredCoverImage; +} + +void ReaderNode::drawCover(ZLPaintContext &context, int vOffset) { + drawCoverReal(context, vOffset); +} + +void ReaderNode::drawCoverReal(ZLPaintContext &context, int vOffset) { + shared_ptr cover = coverImage(); + if (cover.isNull()) { + return; + } + + shared_ptr coverData = ZLImageManager::Instance().imageData(*cover); + if (coverData.isNull()) { + return; + } + + const FBTextStyle &style = FBTextStyle::Instance(); + const int unit = unitSize(context, style); + const int h = unit * 9 / 2, w = h * 3 / 4; + vOffset += unit / 2; + const int hOffset = level() * unit * 3 - unit * 2; + + const int origWidth = context.imageWidth(*coverData); + const int origHeight = context.imageHeight(*coverData); + if (origWidth == 0 || origHeight == 0) { + return; + } + + int coeff = std::min(w / origWidth, h / origHeight); + if (coeff == 0) { + coeff = 1; + } + int width = coeff * origWidth; + int height = coeff * origHeight; + if (width > w || height > h) { + width = context.imageWidth(*coverData, w, h, ZLPaintContext::SCALE_REDUCE_SIZE); + height = context.imageHeight(*coverData, w, h, ZLPaintContext::SCALE_REDUCE_SIZE); + } + context.drawImage(hOffset + (w - width) / 2, vOffset + (h + height) / 2, *coverData, width, height, ZLPaintContext::SCALE_FIT_TO_SIZE); +} + +void ReaderNode::drawTitle(ZLPaintContext &context, int vOffset) { + const FBTextStyle &style = FBTextStyle::Instance(); + const int unit = unitSize(context, style); + const int hOffset = level() * unit * 3 + unit * 2; + + context.setColor(highlighted() ? + FBOptions::Instance().colorOption(ZLTextStyle::HIGHLIGHTED_TEXT).value() : + FBOptions::Instance().RegularTextColorOption.value()); + context.setFont(style.fontFamily(), style.fontSize(), style.bold(), style.italic()); + + const std::string text = title(); + context.drawString(hOffset, vOffset + 2 * unit, text.data(), text.size(), false); +} + +void ReaderNode::drawSummary(ZLPaintContext &context, int vOffset) { + const std::string text = summary(); + if (text.empty()) { + return; + } + + const FBTextStyle &style = FBTextStyle::Instance(); + const int unit = unitSize(context, style); + const int hOffset = level() * unit * 3 + unit * 2; + + context.setColor(highlighted() ? + FBOptions::Instance().colorOption(ZLTextStyle::HIGHLIGHTED_TEXT).value() : + FBOptions::Instance().RegularTextColorOption.value()); + context.setFont(style.fontFamily(), style.fontSize() * 2 / 3, style.bold(), style.italic()); + + context.drawString(hOffset, vOffset + 13 * unit / 4, text.data(), text.size(), false); +} + +void ReaderNode::drawHyperlink(ZLPaintContext &context, int &hOffset, int &vOffset, shared_ptr action, bool auxiliary) { + // auxiliary makes font size and hSkip to be 70% of their normal sizes + if (action.isNull() || !action->makesSense()) { + return; + } + + const FBTextStyle &style = FBTextStyle::Instance(); + const int unit = unitSize(context, style); + const int h = auxiliary ? (unit * 11 / 2) : (unit * 9 / 2); + const int left = hOffset + level() * unit * 3 + unit * 2; + + context.setColor(FBOptions::Instance().colorOption("internal").value()); + context.setFont( + style.fontFamily(), + auxiliary ? (7 * style.fontSize() / 15) : (style.fontSize() * 2 / 3), + style.bold(), + style.italic() + ); + + const std::string text = action->text(resource()); + const int stringW = context.stringWidth(text.data(), text.size(), false); + const int stringH = context.stringHeight(); + context.drawString(left, vOffset + h, text.data(), text.size(), false); + addHyperlink(left, h - stringH, left + stringW, h, action); + hOffset += stringW + 4 * context.spaceWidth(); +} + +ReaderNode::ExpandTreeAction::ExpandTreeAction(ReaderNode &node) : myNode(node) { +} + +void ReaderNode::ExpandTreeAction::run() { + myNode.expandOrCollapseSubtree(); + Reader::Instance().refreshWindow(); +} + +ZLResourceKey ReaderNode::ExpandTreeAction::key() const { + return ZLResourceKey(myNode.isOpen() ? "collapseTree" : "expandTree"); +} + +void ReaderNode::expandOrCollapseSubtree() { + if (isOpen()) { + open(false); + } else if (!children().empty()) { + open(true); + if (view().visibilityMode(this) != ZLBlockTreeView::INVISIBLE) { + ZLBlockTreeNode *lastChild = children().back(); + while (view().visibilityMode(lastChild) != ZLBlockTreeView::VISIBLE && + this != view().firstVisibleNode()) { + view().setFirstVisibleNode(view().firstVisibleNode()->next()); + } + } + } +} + +void ReaderNode::registerAction(shared_ptr action, bool auxiliary) { + if (!action.isNull()) { + myActions.push_back(std::make_pair(action, auxiliary)); + } +} + +void ReaderNode::registerExpandTreeAction() { + registerAction(new ExpandTreeAction(*this)); +} + +shared_ptr ReaderNode::defaultCoverImage(const std::string &id) { + shared_ptr cover = ourDefaultCovers[id]; + if (cover.isNull()) { + cover = new ZLFileImage( + ZLFile(ZLibrary::ApplicationImageDirectory() + ZLibrary::FileNameDelimiter + id), 0 + ); + ourDefaultCovers[id] = cover; + } + return cover; +} + +int ReaderNode::height(ZLPaintContext &context) const { + bool hasAuxHyperlink = false; + for (std::vector,bool> >::const_iterator it = myActions.begin(); it != myActions.end(); ++it) { + if (it->second && it->first->makesSense()) { + hasAuxHyperlink = true; + break; + } + } + return + unitSize(context, FBTextStyle::Instance()) * + (hasAuxHyperlink ? 13 : 11) / 2; +} + +int ReaderNode::unitSize(ZLPaintContext &context, const FBTextStyle &style) const { + context.setFont(style.fontFamily(), style.fontSize(), style.bold(), style.italic()); + return (context.stringHeight() * 2 + 2) / 3; +} + +std::string ReaderNode::summary() const { + std::string result; + int count = 0; + const ZLBlockTreeNode::List &subNodes = children(); + ZLBlockTreeNode::List::const_iterator it = subNodes.begin(); + for (; it != subNodes.end() && count < 3; ++it, ++count) { + if (count > 0) { + result += ", "; + } + result += ((const ReaderNode*)*it)->title(); + } + if (it != subNodes.end()) { + result += ", ..."; + } + return result; +} + +void ReaderNode::paint(ZLPaintContext &context, int vOffset) { + if (!myIsInitialized) { + init(); + myIsInitialized = true; + } + + removeAllHyperlinks(); + + drawCover(context, vOffset); + drawTitle(context, vOffset); + drawSummary(context, vOffset); + + int left = 0; + int auxLeft = 0; + for (std::vector,bool> >::const_iterator it = myActions.begin(); it != myActions.end(); ++it) { + if (it->first->makesSense()) { + if (it->second) { + drawHyperlink(context, auxLeft, vOffset, it->first, true); + } else { + drawHyperlink(context, left, vOffset, it->first); + } + } + } +} diff --git a/reader/src/blockTree/ReaderNode.h b/reader/src/blockTree/ReaderNode.h new file mode 100644 index 0000000..8abb7a1 --- /dev/null +++ b/reader/src/blockTree/ReaderNode.h @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#ifndef __READERNODE_H__ +#define __READERNODE_H__ + +#include +#include + +#include + +class ZLImage; +class ZLResource; +class FBTextStyle; + +class ReaderNode : public ZLBlockTreeNode { + +protected: + static shared_ptr defaultCoverImage(const std::string &id); + +private: + static std::map > ourDefaultCovers; + +private: + class ExpandTreeAction; + +public: + static const ZLTypeId TYPE_ID; + +protected: + ReaderNode(ZLBlockTreeNode *parent, std::size_t atPosition = (std::size_t)-1); + virtual void init(); + virtual const ZLResource &resource() const = 0; + virtual bool highlighted() const; + +public: + ~ReaderNode(); + + void drawCoverReal(ZLPaintContext &context, int vOffset); + +protected: + virtual void drawCover(ZLPaintContext &context, int vOffset); + void drawTitle(ZLPaintContext &context, int vOffset); + void drawSummary(ZLPaintContext &context, int vOffset); + void drawHyperlink(ZLPaintContext &context, int &hOffset, int &vOffset, shared_ptr action, bool auxiliary = false); + +private: + int unitSize(ZLPaintContext &context, const FBTextStyle &style) const; + +protected: + void paint(ZLPaintContext &context, int vOffset); + void registerAction(shared_ptr action, bool auxiliary = false); + void registerExpandTreeAction(); + virtual shared_ptr extractCoverImage() const = 0; + +private: + const ZLTypeId &typeId() const; + +public: + shared_ptr coverImage() const; + virtual std::string title() const = 0; + virtual std::string summary() const; + + void expandOrCollapseSubtree(); + +protected: + int height(ZLPaintContext &context) const; + +private: + mutable bool myCoverImageIsStored; + mutable shared_ptr myStoredCoverImage; + std::vector,bool> > myActions; + bool myIsInitialized; +}; + +#endif /* __READERNODE_H__ */ diff --git a/reader/src/bookmodel/BookModel.cpp b/reader/src/bookmodel/BookModel.cpp new file mode 100644 index 0000000..2123282 --- /dev/null +++ b/reader/src/bookmodel/BookModel.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2004-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 "BookModel.h" +#include "BookReader.h" + +#include "../formats/FormatPlugin.h" +#include "../library/Book.h" + +BookModel::BookModel(const shared_ptr book) : myBook(book) { + myBookTextModel = new ZLTextPlainModel(book->language(), 102400); + myContentsModel = new ContentsModel(book->language()); + shared_ptr plugin = PluginCollection::Instance().plugin(book->file(), false); + if (!plugin.isNull()) { + plugin->readModel(*this); + } +} + +BookModel::~BookModel() { +} + +void BookModel::setHyperlinkMatcher(shared_ptr matcher) { + myHyperlinkMatcher = matcher; +} + +BookModel::Label BookModel::label(const std::string &id) const { + if (!myHyperlinkMatcher.isNull()) { + return myHyperlinkMatcher->match(myInternalHyperlinks, id); + } + + std::map::const_iterator it = myInternalHyperlinks.find(id); + return (it != myInternalHyperlinks.end()) ? it->second : Label(0, -1); +} + +ContentsModel::ContentsModel(const std::string &language) : ZLTextTreeModel(language) { +} + +void ContentsModel::setReference(const ZLTextTreeParagraph *paragraph, int reference) { + myReferenceByParagraph[paragraph] = reference; +} + +int ContentsModel::reference(const ZLTextTreeParagraph *paragraph) const { + std::map::const_iterator it = myReferenceByParagraph.find(paragraph); + return (it != myReferenceByParagraph.end()) ? it->second : -1; +} + +const shared_ptr BookModel::book() const { + return myBook; +} diff --git a/reader/src/bookmodel/BookModel.h b/reader/src/bookmodel/BookModel.h new file mode 100644 index 0000000..6f83728 --- /dev/null +++ b/reader/src/bookmodel/BookModel.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __BOOKMODEL_H__ +#define __BOOKMODEL_H__ + +#include +#include + +#include +#include +#include + +class ZLImage; +class Book; + +class ContentsModel : public ZLTextTreeModel { + +public: + ContentsModel(const std::string &language); + void setReference(const ZLTextTreeParagraph *paragraph, int reference); + int reference(const ZLTextTreeParagraph *paragraph) const; + +private: + std::map myReferenceByParagraph; +}; + +class BookModel : public ZLUserDataHolder { + +public: + struct Label { + Label(shared_ptr model, int paragraphNumber) : Model(model), ParagraphNumber(paragraphNumber) {} + + const shared_ptr Model; + const int ParagraphNumber; + }; + +public: + class HyperlinkMatcher { + + public: + virtual Label match(const std::map &lMap, const std::string &id) const = 0; + }; + +public: + BookModel(const shared_ptr book); + ~BookModel(); + + void setHyperlinkMatcher(shared_ptr matcher); + + shared_ptr bookTextModel() const; + shared_ptr contentsModel() const; + + const ZLImageMap &imageMap() const; + Label label(const std::string &id) const; + + const shared_ptr book() const; + +private: + const shared_ptr myBook; + shared_ptr myBookTextModel; + shared_ptr myContentsModel; + ZLImageMap myImages; + std::map > myFootnotes; + std::map myInternalHyperlinks; + shared_ptr myHyperlinkMatcher; + +friend class BookReader; +}; + +inline shared_ptr BookModel::bookTextModel() const { return myBookTextModel; } +inline shared_ptr BookModel::contentsModel() const { return myContentsModel; } +inline const ZLImageMap &BookModel::imageMap() const { return myImages; } + +#endif /* __BOOKMODEL_H__ */ diff --git a/reader/src/bookmodel/BookReader.cpp b/reader/src/bookmodel/BookReader.cpp new file mode 100644 index 0000000..2982c43 --- /dev/null +++ b/reader/src/bookmodel/BookReader.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2004-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 "BookReader.h" +#include "BookModel.h" + +#include "../library/Book.h" + +BookReader::BookReader(BookModel &model) : myModel(model) { + myCurrentTextModel = 0; + myLastTOCParagraphIsEmpty = false; + + myContentsParagraphExists = false; + + myInsideTitle = false; + mySectionContainsRegularContents = false; +} + +BookReader::~BookReader() { +} + +void BookReader::setMainTextModel() { + myCurrentTextModel = myModel.myBookTextModel; +} + +void BookReader::setFootnoteTextModel(const std::string &id) { + std::map >::iterator it = myModel.myFootnotes.find(id); + if (it != myModel.myFootnotes.end()) { + myCurrentTextModel = (*it).second; + } else { + myCurrentTextModel = new ZLTextPlainModel(myModel.myBookTextModel->language(), 8192); + myModel.myFootnotes.insert(std::make_pair(id, myCurrentTextModel)); + } +} + +bool BookReader::paragraphIsOpen() const { + if (myCurrentTextModel.isNull()) { + return false; + } + for (std::list >::const_iterator it = myModelsWithOpenParagraphs.begin(); it != myModelsWithOpenParagraphs.end(); ++it) { + if (*it == myCurrentTextModel) { + return true; + } + } + return false; +} + +void BookReader::unsetTextModel() { + myCurrentTextModel.reset(); +} + +void BookReader::pushKind(FBTextKind kind) { + myKindStack.push_back(kind); +} + +bool BookReader::popKind() { + if (!myKindStack.empty()) { + myKindStack.pop_back(); + return true; + } + return false; +} + +bool BookReader::isKindStackEmpty() const { + return myKindStack.empty(); +} + +void BookReader::beginParagraph(ZLTextParagraph::Kind kind) { + endParagraph(); + if (myCurrentTextModel != 0) { + ((ZLTextPlainModel&)*myCurrentTextModel).createParagraph(kind); + for (std::vector::const_iterator it = myKindStack.begin(); it != myKindStack.end(); ++it) { + myCurrentTextModel->addControl(*it, true); + } + if (!myHyperlinkReference.empty()) { + myCurrentTextModel->addHyperlinkControl(myHyperlinkKind, myHyperlinkType, myHyperlinkReference); + } + myModelsWithOpenParagraphs.push_back(myCurrentTextModel); + } +} + +void BookReader::endParagraph() { + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myModelsWithOpenParagraphs.remove(myCurrentTextModel); + } +} + +void BookReader::addControl(FBTextKind kind, bool start) { + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addControl(kind, start); + } + if (!start && !myHyperlinkReference.empty() && (kind == myHyperlinkKind)) { + myHyperlinkReference.erase(); + } +} + +void BookReader::addStyleEntry(const ZLTextStyleEntry &entry) { + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addStyleEntry(entry); + } +} + +void BookReader::addStyleCloseEntry() { + addControl(REGULAR, false); //used instead in XHTMLReader + //TODO implement ZLTextModel::addStyleCloseEntry() +// if (paragraphIsOpen()) { +// flushTextBufferToParagraph(); +// myCurrentTextModel->addStyleCloseEntry(); +// } +} + +void BookReader::addFixedHSpace(unsigned char length) { + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addFixedHSpace(length); + } +} + +void BookReader::addHyperlinkControl(FBTextKind kind, const std::string &label) { + myHyperlinkKind = kind; + std::string type; + switch (myHyperlinkKind) { + case INTERNAL_HYPERLINK: + case FOOTNOTE: + myHyperlinkType = HYPERLINK_INTERNAL; + type = "internal"; + break; + case EXTERNAL_HYPERLINK: + myHyperlinkType = HYPERLINK_EXTERNAL; + type = "external"; + break; + case BOOK_HYPERLINK: + myHyperlinkType = HYPERLINK_BOOK; + type = "book"; + break; + default: + myHyperlinkType = HYPERLINK_NONE; + break; + } + ZLLogger::Instance().println( + "hyperlink", + " + control (" + type + "): " + label + ); + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addHyperlinkControl(kind, myHyperlinkType, label); + } + myHyperlinkReference = label; +} + +void BookReader::addHyperlinkLabel(const std::string &label) { + if (!myCurrentTextModel.isNull()) { + int paragraphNumber = myCurrentTextModel->paragraphsNumber(); + if (paragraphIsOpen()) { + --paragraphNumber; + } + addHyperlinkLabel(label, paragraphNumber); + } +} + +void BookReader::addHyperlinkLabel(const std::string &label, int paragraphNumber) { + ZLLogger::Instance().println( + "hyperlink", + " + label: " + label + ); + myModel.myInternalHyperlinks.insert(std::make_pair( + label, BookModel::Label(myCurrentTextModel, paragraphNumber) + )); +} + +void BookReader::addData(const std::string &data) { + if (!data.empty() && paragraphIsOpen()) { + if (!myInsideTitle) { + mySectionContainsRegularContents = true; + } + myBuffer.push_back(data); + } +} + +void BookReader::addContentsData(const std::string &data) { + if (!data.empty() && !myTOCStack.empty()) { + myContentsBuffer.push_back(data); + } +} + +void BookReader::flushTextBufferToParagraph() { + myCurrentTextModel->addText(myBuffer); + myBuffer.clear(); +} + +void BookReader::addImage(const std::string &id, shared_ptr image) { + myModel.myImages[id] = image; +} + +void BookReader::insertEndParagraph(ZLTextParagraph::Kind kind) { + if ((myCurrentTextModel != 0) && mySectionContainsRegularContents) { + std::size_t size = myCurrentTextModel->paragraphsNumber(); + if ((size > 0) && (((*myCurrentTextModel)[(std::size_t)-1])->kind() != kind)) { + ((ZLTextPlainModel&)*myCurrentTextModel).createParagraph(kind); + mySectionContainsRegularContents = false; + } + } +} + +void BookReader::insertEndOfSectionParagraph() { + insertEndParagraph(ZLTextParagraph::END_OF_SECTION_PARAGRAPH); +} + +void BookReader::insertEndOfTextParagraph() { + insertEndParagraph(ZLTextParagraph::END_OF_TEXT_PARAGRAPH); +} + +void BookReader::addImageReference(const std::string &id, short vOffset) { + if (myCurrentTextModel != 0) { + mySectionContainsRegularContents = true; + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addImage(id, myModel.imageMap(), vOffset); + } else { + beginParagraph(); + myCurrentTextModel->addControl(IMAGE, true); + myCurrentTextModel->addImage(id, myModel.imageMap(), vOffset); + myCurrentTextModel->addControl(IMAGE, false); + endParagraph(); + } + } +} + +void BookReader::beginContentsParagraph(int referenceNumber) { + if (myCurrentTextModel == myModel.myBookTextModel) { + ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel; + if (referenceNumber == -1) { + referenceNumber = myCurrentTextModel->paragraphsNumber(); + } + ZLTextTreeParagraph *peek = myTOCStack.empty() ? 0 : myTOCStack.top(); + if (!myContentsBuffer.empty()) { + contentsModel.addText(myContentsBuffer); + myContentsBuffer.clear(); + myLastTOCParagraphIsEmpty = false; + } + if (myLastTOCParagraphIsEmpty) { + contentsModel.addText("..."); + } + ZLTextTreeParagraph *para = contentsModel.createParagraph(peek); + contentsModel.addControl(CONTENTS_TABLE_ENTRY, true); + contentsModel.setReference(para, referenceNumber); + myTOCStack.push(para); + myLastTOCParagraphIsEmpty = true; + myContentsParagraphExists = true; + } +} + +void BookReader::endContentsParagraph() { + if (!myTOCStack.empty()) { + ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel; + if (!myContentsBuffer.empty()) { + contentsModel.addText(myContentsBuffer); + myContentsBuffer.clear(); + myLastTOCParagraphIsEmpty = false; + } + if (myLastTOCParagraphIsEmpty) { + contentsModel.addText("..."); + myLastTOCParagraphIsEmpty = false; + } + myTOCStack.pop(); + } + myContentsParagraphExists = false; +} + +void BookReader::setReference(std::size_t contentsParagraphNumber, int referenceNumber) { + ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel; + if (contentsParagraphNumber >= contentsModel.paragraphsNumber()) { + return; + } + contentsModel.setReference((const ZLTextTreeParagraph*)contentsModel[contentsParagraphNumber], referenceNumber); +} + +void BookReader::reset() { + myKindStack.clear(); +} diff --git a/reader/src/bookmodel/BookReader.h b/reader/src/bookmodel/BookReader.h new file mode 100644 index 0000000..3a27262 --- /dev/null +++ b/reader/src/bookmodel/BookReader.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __BOOKREADER_H__ +#define __BOOKREADER_H__ + +#include +#include +#include +#include + +#include + +#include "FBHyperlinkType.h" +#include "FBTextKind.h" + +class BookModel; +class ZLTextModel; +class ZLInputStream; +class ZLTextStyleEntry; + +class BookReader { + +public: + BookReader(BookModel &model); + virtual ~BookReader(); + + void setMainTextModel(); + void setFootnoteTextModel(const std::string &id); + void unsetTextModel(); + + void insertEndOfSectionParagraph(); + void insertEndOfTextParagraph(); + + void pushKind(FBTextKind kind); + bool popKind(); + bool isKindStackEmpty() const; + + void beginParagraph(ZLTextParagraph::Kind kind = ZLTextParagraph::TEXT_PARAGRAPH); + void endParagraph(); + bool paragraphIsOpen() const; + void addControl(FBTextKind kind, bool start); + void addStyleEntry(const ZLTextStyleEntry &entry); + void addStyleCloseEntry(); //TODO reimplement + void addHyperlinkControl(FBTextKind kind, const std::string &label); + void addHyperlinkLabel(const std::string &label); + void addHyperlinkLabel(const std::string &label, int paragraphNumber); + void addFixedHSpace(unsigned char length); + + void addImageReference(const std::string &id, short vOffset = 0); + void addImage(const std::string &id, shared_ptr image); + + void beginContentsParagraph(int referenceNumber = -1); + void endContentsParagraph(); + bool contentsParagraphIsOpen() const; + void setReference(std::size_t contentsParagraphNumber, int referenceNumber); + + void addData(const std::string &data); + void addContentsData(const std::string &data); + + void enterTitle() { myInsideTitle = true; } + void exitTitle() { myInsideTitle = false; } + + const BookModel &model() const { return myModel; } + + void reset(); + +private: + void insertEndParagraph(ZLTextParagraph::Kind kind); + void flushTextBufferToParagraph(); + +private: + BookModel &myModel; + shared_ptr myCurrentTextModel; + std::list > myModelsWithOpenParagraphs; + + std::vector myKindStack; + + bool myContentsParagraphExists; + std::stack myTOCStack; + bool myLastTOCParagraphIsEmpty; + + bool mySectionContainsRegularContents; + bool myInsideTitle; + + std::vector myBuffer; + std::vector myContentsBuffer; + + std::string myHyperlinkReference; + FBHyperlinkType myHyperlinkType; + FBTextKind myHyperlinkKind; +}; + +inline bool BookReader::contentsParagraphIsOpen() const { + return myContentsParagraphExists; +} + +#endif /* __BOOKREADER_H__ */ diff --git a/reader/src/bookmodel/FBHyperlinkType.h b/reader/src/bookmodel/FBHyperlinkType.h new file mode 100644 index 0000000..fac7d80 --- /dev/null +++ b/reader/src/bookmodel/FBHyperlinkType.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011-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. + */ + +#ifndef __FBHYPERLINKTYPE_H__ +#define __FBHYPERLINKTYPE_H__ + +enum FBHyperlinkType { + HYPERLINK_NONE = 0, + HYPERLINK_INTERNAL = 1, + HYPERLINK_EXTERNAL = 2, + HYPERLINK_BOOK = 3, +}; + +#endif /* __FBHYPERLINKTYPE_H__ */ diff --git a/reader/src/bookmodel/FBTextKind.h b/reader/src/bookmodel/FBTextKind.h new file mode 100644 index 0000000..746db2b --- /dev/null +++ b/reader/src/bookmodel/FBTextKind.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __FBTEXTKIND_H__ +#define __FBTEXTKIND_H__ + +enum FBTextKind { + // please, don't change these numbers + // add new text kinds at end of this enumeration + // + // all the values MUST be in the range 0..127 + REGULAR = 0, + TITLE = 1, + SECTION_TITLE = 2, + POEM_TITLE = 3, + SUBTITLE = 4, + ANNOTATION = 5, + EPIGRAPH = 6, + STANZA = 7, + VERSE = 8, + PREFORMATTED = 9, + IMAGE = 10, + END_OF_SECTION = 11, + CITE = 12, + AUTHOR = 13, + DATEKIND = 14, + INTERNAL_HYPERLINK = 15, + FOOTNOTE = 16, + EMPHASIS = 17, + STRONG = 18, + SUB = 19, + SUP = 20, + CODE = 21, + STRIKETHROUGH = 22, + CONTENTS_TABLE_ENTRY = 23, + //LIBRARY_AUTHOR_ENTRY = 24, + //LIBRARY_BOOK_ENTRY = 25, + LIBRARY_ENTRY = 25, + //RECENT_BOOK_LIST = 26, + ITALIC = 27, + BOLD = 28, + DEFINITION = 29, + DEFINITION_DESCRIPTION = 30, + H1 = 31, + H2 = 32, + H3 = 33, + H4 = 34, + H5 = 35, + H6 = 36, + EXTERNAL_HYPERLINK = 37, + BOOK_HYPERLINK = 38, +}; + +#endif /* __FBTEXTKIND_H__ */ diff --git a/reader/src/database/booksdb/BooksDB.cpp b/reader/src/database/booksdb/BooksDB.cpp new file mode 100644 index 0000000..bf6d2b3 --- /dev/null +++ b/reader/src/database/booksdb/BooksDB.cpp @@ -0,0 +1,503 @@ +/* + * 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; +} diff --git a/reader/src/database/booksdb/BooksDB.h b/reader/src/database/booksdb/BooksDB.h new file mode 100644 index 0000000..282a0aa --- /dev/null +++ b/reader/src/database/booksdb/BooksDB.h @@ -0,0 +1,174 @@ +/* + * 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. + */ + +#ifndef __BOOKSDB_H__ +#define __BOOKSDB_H__ + +#include +#include +#include + +#include "../sqldb/implsqlite/SQLiteDataBase.h" +#include "DBRunnables.h" + +#include "../../reader/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 ourInstance; + + BooksDB(const std::string &path); + +public: + virtual ~BooksDB(); + +public: + bool initDatabase(); + bool isInitialized() const; + bool clearDatabase(); + + shared_ptr loadBook(const std::string &fileName); + bool saveBook(const shared_ptr book); + bool saveAuthors(const shared_ptr book); + bool saveSeries(const shared_ptr book); + bool saveTags(const shared_ptr 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 &entries); + bool saveFileEntries(const std::string &fileName, const std::vector &entries); + + bool loadRecentBooks(std::vector &fileNames); + bool saveRecentBooks(const BookList &books); + + bool loadBooks(BookList &books); + + bool loadBookStateStack(const Book &book, std::deque &stack); + bool saveBookStateStack(const Book &book, const std::deque &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 getTagById(int id) const; + void loadAllTagsById() const; + +private: + void loadSeries(Book &book); + void loadSeries(const std::map > &books); + void loadAuthors(Book &book); + void loadAuthors(const std::map > &books); + void loadTags(Book &book); + void loadTags(const std::map > &books); + + std::string getFileName(int fileId); + +private: + void initCommands(); + +private: + bool myInitialized; + + shared_ptr mySaveTableBook; + shared_ptr mySaveAuthors; + shared_ptr mySaveSeries; + shared_ptr mySaveTags; + shared_ptr mySaveBook; + shared_ptr mySaveFileEntries; + + shared_ptr myFindFileId; + + shared_ptr myLoadFileEntries; + + shared_ptr myLoadRecentBooks; + shared_ptr mySaveRecentBooks; + + shared_ptr mySaveBookStateStack; + + shared_ptr myDeleteBook; + + shared_ptr myLoadNetworkLinks; + shared_ptr myFindNetworkLinkId; + shared_ptr myDeleteNetworkLink; + shared_ptr myDeleteNetworkLinkUrls; + shared_ptr myLoadNetworkLinkUrls; + + shared_ptr myLoadBook; + + shared_ptr myGetFileSize; + shared_ptr mySetFileSize; + + shared_ptr myFindFileName; + + shared_ptr myFindAuthorId; + + shared_ptr myLoadBooks; + + shared_ptr myLoadBookStateStack; + + shared_ptr myGetPalmType; + shared_ptr mySetPalmType; + + shared_ptr myGetNetFile; + shared_ptr mySetNetFile; + + shared_ptr myLoadStackPos; + shared_ptr mySetStackPos; + shared_ptr myLoadBookState; + shared_ptr mySetBookState; + + shared_ptr myInsertBookList; + shared_ptr myDeleteBookList; + shared_ptr 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/reader/src/database/booksdb/BooksDBQuery.cpp b/reader/src/database/booksdb/BooksDBQuery.cpp new file mode 100644 index 0000000..134e43b --- /dev/null +++ b/reader/src/database/booksdb/BooksDBQuery.cpp @@ -0,0 +1,327 @@ +/* + * 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 "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/reader/src/database/booksdb/BooksDBQuery.h b/reader/src/database/booksdb/BooksDBQuery.h new file mode 100644 index 0000000..c202dea --- /dev/null +++ b/reader/src/database/booksdb/BooksDBQuery.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef __BOOKSDBQUERY_H__ +#define __BOOKSDBQUERY_H__ + +#include + +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/reader/src/database/booksdb/BooksDBUtil.cpp b/reader/src/database/booksdb/BooksDBUtil.cpp new file mode 100644 index 0000000..3a7de96 --- /dev/null +++ b/reader/src/database/booksdb/BooksDBUtil.cpp @@ -0,0 +1,184 @@ +/* + * 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 "BooksDBUtil.h" +#include "BooksDB.h" + +#include "../../library/Book.h" +#include "../../library/Tag.h" +#include "../../library/Author.h" + +shared_ptr 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 = loadFromDB(filePath); + if (!book.isNull() && isBookFull(*book)) { + return book; + } + } else { + if (physicalFilePath != filePath) { + resetZipInfo(file); + } + saveInfo(file); + } + + shared_ptr book = Book::loadFromFile(ZLFile(filePath)); + if (book.isNull()) { + return 0; + } + BooksDB::Instance().saveBook(book); + return book; +} + +bool BooksDBUtil::getRecentBooks(BookList &books) { + std::vector fileNames; + if (!BooksDB::Instance().loadRecentBooks(fileNames)) { + return false; + } + for (std::vector::const_iterator it = fileNames.begin(); it != fileNames.end(); ++it) { + shared_ptr book = getBook(*it /*, true OR false ? */); // TODO: check file ??? + if (!book.isNull()) { + books.push_back(book); + } + } + return true; +} + +bool BooksDBUtil::getBooks(std::map > &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 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 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 &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 zipDir = zipFile.directory(); + if (!zipDir.isNull()) { + std::vector 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 zipDir = zipFile.directory(); + if (zipDir.isNull()) { + return false; + } + std::vector 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, shared_ptr tag) { + if (book->addTag(tag)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::renameTag(shared_ptr book, shared_ptr from, shared_ptr to, bool includeSubTags) { + if (book->renameTag(from, to, includeSubTags)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::cloneTag(shared_ptr book, shared_ptr from, shared_ptr to, bool includeSubTags) { + if (book->cloneTag(from, to, includeSubTags)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::removeAllTags(shared_ptr book) { + book->removeAllTags(); +} diff --git a/reader/src/database/booksdb/BooksDBUtil.h b/reader/src/database/booksdb/BooksDBUtil.h new file mode 100644 index 0000000..8d55e1c --- /dev/null +++ b/reader/src/database/booksdb/BooksDBUtil.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef __BOOKSDBUTIL_H__ +#define __BOOKSDBUTIL_H__ + +#include + +#include + +#include "../../library/Lists.h" + +class Book; +class ZLFile; + +class BooksDBUtil { + +public: + static shared_ptr getBook(const std::string &fileName, bool checkFile = true); + + static bool getBooks(std::map > &booksmap, bool checkFile = true); + + static bool getRecentBooks(BookList &books); + +public: + static void addTag(shared_ptr book, shared_ptr tag); + static void renameTag(shared_ptr book, shared_ptr from, shared_ptr to, bool includeSubTags); + static void cloneTag(shared_ptr book, shared_ptr from, shared_ptr to, bool includeSubTags); + static void removeAllTags(shared_ptr book); + + static bool checkInfo(const ZLFile &file); + static void saveInfo(const ZLFile &file); + + static void listZipEntries(const ZLFile &zipFile, std::vector &entries); + static void resetZipInfo(const ZLFile &zipFile); + + static bool isBookFull(const Book &book); + + static bool canRemoveFile(const std::string &fileName); + +private: + static shared_ptr loadFromDB(const std::string &fileName); +}; + +#endif /* __BOOKSDBUTIL_H__ */ diff --git a/reader/src/database/booksdb/BooksDB_BookAuthor.cpp b/reader/src/database/booksdb/BooksDB_BookAuthor.cpp new file mode 100644 index 0000000..8cdc2a4 --- /dev/null +++ b/reader/src/database/booksdb/BooksDB_BookAuthor.cpp @@ -0,0 +1,76 @@ +/* + * 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 "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 command = SQLiteFactory::createCommand( + LOAD_AUTHORS_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr 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 > &books) { + static shared_ptr command = SQLiteFactory::createCommand( + LOAD_ALL_AUTHORS_QUERY, connection() + ); + shared_ptr reader = command->executeReader(); + + for (std::map >::const_iterator it = books.begin(); it != books.end(); ++it) { + it->second->removeAllAuthors(); + } + + while (reader->next()) { + std::map >::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/reader/src/database/booksdb/BooksDB_BookSeries.cpp b/reader/src/database/booksdb/BooksDB_BookSeries.cpp new file mode 100644 index 0000000..a9b860b --- /dev/null +++ b/reader/src/database/booksdb/BooksDB_BookSeries.cpp @@ -0,0 +1,83 @@ +/* + * 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 "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 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 command = SQLiteFactory::createCommand( + LOAD_SERIES_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr 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 > &books) { + shared_ptr command = SQLiteFactory::createCommand( + LOAD_ALL_SERIES_QUERY, connection() + ); + shared_ptr reader = command->executeReader(); + + while (reader->next()) { + std::string seriesTitle = reader->textValue(0, std::string()); + std::map >::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/reader/src/database/booksdb/BooksDB_BookTag.cpp b/reader/src/database/booksdb/BooksDB_BookTag.cpp new file mode 100644 index 0000000..a3d36fe --- /dev/null +++ b/reader/src/database/booksdb/BooksDB_BookTag.cpp @@ -0,0 +1,246 @@ +/* + * 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 "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 command = SQLiteFactory::createCommand( + LOAD_BOOK_TAGS_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr reader = command->executeReader(); + + book.removeAllTags(); + while (reader->next()) { + book.addTag(getTagById(reader->intValue(0))); + } +} + +void BooksDB::loadTags(const std::map > &books) { + loadAllTagsById(); + + static shared_ptr command = SQLiteFactory::createCommand( + LOAD_ALL_BOOK_TAGS_QUERY, connection() + ); + shared_ptr reader = command->executeReader(); + + for (std::map >::const_iterator it = books.begin(); it != books.end(); ++it) { + it->second->removeAllTags(); + } + + while (reader->next()) { + std::map >::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 BooksDB::getTagById(int id) const { + if (id == 0) { + return 0; + } + + shared_ptr tag = Tag::getTagById(id); + if (!tag.isNull()) { + return tag; + } + + static shared_ptr command = SQLiteFactory::createCommand( + LOAD_SINGLE_TAG_QUERY, connection(), "@tag_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@tag_id").value()) = id; + shared_ptr 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 command = SQLiteFactory::createCommand( + LOAD_ALL_TAGS_QUERY, connection() + ); + shared_ptr 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 reader = myLoadBookTags->executeReader(); + + while (reader->next()) { + shared_ptr 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 = (*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) { + 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 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) { + myBook = book; +} diff --git a/reader/src/database/booksdb/DBRunnables.h b/reader/src/database/booksdb/DBRunnables.h new file mode 100644 index 0000000..dcbe438 --- /dev/null +++ b/reader/src/database/booksdb/DBRunnables.h @@ -0,0 +1,311 @@ +/* + * 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. + */ + +#ifndef __DBRUNNABLES_H__ +#define __DBRUNNABLES_H__ + +#include +#include + +#include "../../reader/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); + +private: + bool addTableBook(const shared_ptr book, int fileId); + bool updateTableBook(const shared_ptr book); + +private: + shared_ptr myBook; + + shared_ptr myFindBookId; + + shared_ptr myAddBook; + shared_ptr myUpdateBook; + + shared_ptr myFindFileId; +}; + +class SaveAuthorsRunnable : public DBRunnable { + +public: + SaveAuthorsRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr book); + +private: + shared_ptr myBook; + + shared_ptr mySetBookAuthor; + shared_ptr myTrimBookAuthors; + + shared_ptr myFindAuthorId; + shared_ptr myAddAuthor; +}; + +class SaveTagsRunnable : public DBRunnable { + +public: + SaveTagsRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr book); + +private: + int findTagId(shared_ptr tag); + +private: + shared_ptr myBook; + + shared_ptr myAddBookTag; + shared_ptr myDeleteBookTag; + + shared_ptr myFindTagId; + shared_ptr myAddTag; + + shared_ptr myLoadBookTags; +}; + + +class SaveSeriesRunnable : public DBRunnable { + +public: + SaveSeriesRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr book); + +private: + shared_ptr myBook; + + shared_ptr mySetBookSeries; + shared_ptr myDeleteBookSeries; + + shared_ptr myFindSeriesId; + shared_ptr myAddSeries; +}; + +class SaveBookRunnable : public DBRunnable { +public: + SaveBookRunnable(SaveTableBookRunnable &saveTableBook, SaveAuthorsRunnable &saveAuthors, + SaveSeriesRunnable &saveSeries, SaveTagsRunnable &saveTags); + + bool run(); + void setBook(shared_ptr 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 &entries); + +private: + std::string myFileName; + std::vector myEntries; + + shared_ptr myAddFile; + + shared_ptr myFindFileId; + shared_ptr myDeleteFileEntries; +}; + +class SaveRecentBooksRunnable : public DBRunnable { + +public: + SaveRecentBooksRunnable(DBConnection &connection); + bool run(); + void setBooks(const BookList &books); + +private: + BookList myBooks; + + shared_ptr myClearRecentBooks; + shared_ptr myInsertRecentBooks; +}; + +class SaveBookStateStackRunnable : public DBRunnable { + +public: + SaveBookStateStackRunnable(DBConnection &connection); + bool run(); + void setState(int bookId, const std::deque &stack); + +private: + int myBookId; + std::deque myStack; + + shared_ptr myTrimBookStateStack; + shared_ptr 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 myDeleteFileEntries; + shared_ptr myLoadFileEntryIds; +}; + +class DeleteBookRunnable : public DBRunnable { + +public: + DeleteBookRunnable(DBConnection &connection); + bool run(); + void setFileName(const std::string &fileName); + +private: + std::string myFileName; + + shared_ptr myFindFileId; + shared_ptr 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 &entries) { + myFileName = fileName; + myEntries = entries; // copy vector +} + +inline void SaveBookStateStackRunnable::setState(int bookId, const std::deque &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 myFindFileId; + shared_ptr myAddFile; +}; + + +/* + * Load Runnables + */ + +class LoadFileEntriesRunnable : public DBRunnable { + +public: + LoadFileEntriesRunnable(DBConnection &connection); + bool run(); + void setFileName(const std::string &fileName); + void collectEntries(std::vector &entries); + +private: + std::string myFileName; + std::vector myEntries; + + shared_ptr myFindFileId; + + shared_ptr myLoadFileEntries; +}; + +class LoadRecentBooksRunnable : public DBRunnable { + +public: + LoadRecentBooksRunnable(DBConnection &connection); + bool run(); + void collectFileIds(std::vector &fileIds); + +private: + std::vector myFileIds; + + shared_ptr myLoadRecentBooks; +}; + +#endif /* __DBRUNNABLES_H__ */ diff --git a/reader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp b/reader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp new file mode 100644 index 0000000..c0c9063 --- /dev/null +++ b/reader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp @@ -0,0 +1,46 @@ +/* + * 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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool ClearBooksDBRunnable::run() { + shared_ptr 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/reader/src/database/booksdb/runnables/DeleteBookRunnable.cpp b/reader/src/database/booksdb/runnables/DeleteBookRunnable.cpp new file mode 100644 index 0000000..e9e2c14 --- /dev/null +++ b/reader/src/database/booksdb/runnables/DeleteBookRunnable.cpp @@ -0,0 +1,37 @@ +/* + * 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 "../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/reader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp b/reader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp new file mode 100644 index 0000000..065a4c2 --- /dev/null +++ b/reader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp @@ -0,0 +1,60 @@ +/* + * 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 "../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 reader = myLoadFileEntryIds->executeReader(); + if (reader.isNull()) { + return false; + } + + std::vector 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::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/reader/src/database/booksdb/runnables/FindFileIdRunnable.cpp b/reader/src/database/booksdb/runnables/FindFileIdRunnable.cpp new file mode 100644 index 0000000..4461c4f --- /dev/null +++ b/reader/src/database/booksdb/runnables/FindFileIdRunnable.cpp @@ -0,0 +1,123 @@ +/* + * 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 "../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 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::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/reader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp b/reader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp new file mode 100644 index 0000000..b8a4b01 --- /dev/null +++ b/reader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp @@ -0,0 +1,40 @@ +/* + * 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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool InitBooksDBRunnable::run() { + shared_ptr 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/reader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp b/reader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp new file mode 100644 index 0000000..3668b83 --- /dev/null +++ b/reader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp @@ -0,0 +1,68 @@ +/* + * 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 "../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 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 &entries) { + myEntries.swap(entries); + myEntries.clear(); +} diff --git a/reader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp b/reader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp new file mode 100644 index 0000000..06e6f79 --- /dev/null +++ b/reader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp @@ -0,0 +1,51 @@ +/* + * 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 "../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 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 &fileIds) { + myFileIds.swap(fileIds); + myFileIds.clear(); +} diff --git a/reader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp b/reader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp new file mode 100644 index 0000000..7336a74 --- /dev/null +++ b/reader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp @@ -0,0 +1,75 @@ +/* + * 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 "../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 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) { + myBook = book; +} diff --git a/reader/src/database/booksdb/runnables/SaveBookRunnable.cpp b/reader/src/database/booksdb/runnables/SaveBookRunnable.cpp new file mode 100644 index 0000000..7cf8dff --- /dev/null +++ b/reader/src/database/booksdb/runnables/SaveBookRunnable.cpp @@ -0,0 +1,43 @@ +/* + * 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 "../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) { + mySaveTableBook.setBook(book); + mySaveAuthors.setBook(book); + mySaveTags.setBook(book); + mySaveSeries.setBook(book); +} diff --git a/reader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp b/reader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp new file mode 100644 index 0000000..85d4a89 --- /dev/null +++ b/reader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp @@ -0,0 +1,56 @@ +/* + * 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 "../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/reader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp b/reader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp new file mode 100644 index 0000000..1c355ed --- /dev/null +++ b/reader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp @@ -0,0 +1,49 @@ +/* + * 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 "../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 = (*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/reader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp b/reader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp new file mode 100644 index 0000000..e56777b --- /dev/null +++ b/reader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp @@ -0,0 +1,58 @@ +/* + * 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 "../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 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) { + myBook = book; +} diff --git a/reader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp b/reader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp new file mode 100644 index 0000000..770963e --- /dev/null +++ b/reader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp @@ -0,0 +1,80 @@ +/* + * 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 "../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 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, 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 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) { + ((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) { + myBook = book; +} diff --git a/reader/src/database/networkdb/DBRunnables.h b/reader/src/database/networkdb/DBRunnables.h new file mode 100644 index 0000000..e9633f6 --- /dev/null +++ b/reader/src/database/networkdb/DBRunnables.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __DBRUNNABLES_H__ +#define __DBRUNNABLES_H__ + +#include "../sqldb/DBConnection.h" +#include "../sqldb/DBCommand.h" +#include "../sqldb/DBRunnable.h" + +#include "NetworkDBQuery.h" + +#include "../../network/NetworkLink.h" +#include "../../network/opds/OPDSLink.h" + +class InitNetworkDBRunnable : public DBRunnable { + +public: + InitNetworkDBRunnable(DBConnection &connection); + bool run(); + +private: + DBConnection &myConnection; +}; + +class ClearNetworkDBRunnable : public DBRunnable { + +public: + ClearNetworkDBRunnable(DBConnection &connection); + bool run(); + +private: + DBConnection &myConnection; +}; + +class SaveNetworkLinkRunnable : public DBRunnable { + +public: + SaveNetworkLinkRunnable(DBConnection &connection); + bool run(); + void setNetworkLink(shared_ptr link); + +private: + bool addNetworkLink(); + bool updateNetworkLink(int linkId); + bool updateNetworkLinkUrls(int linkId); + +private: + shared_ptr myNetworkLink; + + shared_ptr myFindNetworkLinkId; + shared_ptr myAddNetworkLink; + shared_ptr myUpdateNetworkLink; + + shared_ptr myFindNetworkLinkUrls; + shared_ptr myAddNetworkLinkUrl; + shared_ptr myUpdateNetworkLinkUrl; + shared_ptr myDeleteNetworkLinkUrl; + +}; + +inline InitNetworkDBRunnable::InitNetworkDBRunnable(DBConnection &connection) : myConnection(connection) {} +inline ClearNetworkDBRunnable::ClearNetworkDBRunnable(DBConnection &connection) : myConnection(connection) {} + +#endif /* __DBRUNNABLES_H__ */ diff --git a/reader/src/database/networkdb/NetworkDB.cpp b/reader/src/database/networkdb/NetworkDB.cpp new file mode 100644 index 0000000..f422643 --- /dev/null +++ b/reader/src/database/networkdb/NetworkDB.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2004-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 "NetworkDB.h" + +shared_ptr NetworkDB::ourInstance = 0; + +const std::string NetworkDB::DATABASE_NAME = "network.db"; + +NetworkDB &NetworkDB::Instance() { + if (ourInstance.isNull()) { + ZLFile dir(databaseDirName()); + dir.directory(true); + ZLFile file(databaseDirName() + ZLibrary::FileNameDelimiter + DATABASE_NAME); + ourInstance = new NetworkDB(file.physicalFilePath()); + ourInstance->initDatabase(); + } + return *ourInstance; +} + +NetworkDB::NetworkDB(const std::string &path) : SQLiteDataBase(path), myInitialized(false) { + initCommands(); +} + +NetworkDB::~NetworkDB() { +} + +bool NetworkDB::initDatabase() { + if (isInitialized()) { + return true; + } + + if (!open()) { + return false; + } + + myInitialized = true; + + shared_ptr runnable = new InitNetworkDBRunnable(connection()); + if (!executeAsTransaction(*runnable)) { + myInitialized = false; + close(); + return false; + } + + return true; +} + +void NetworkDB::initCommands() { + myLoadNetworkLinks = SQLiteFactory::createCommand(NetworkDBQuery::LOAD_NETWORK_LINKS, connection()); + myLoadNetworkLinkUrls = SQLiteFactory::createCommand(NetworkDBQuery::LOAD_NETWORK_LINKURLS, connection(), "@link_id", DBValue::DBINT); + myFindNetworkLinkId = SQLiteFactory::createCommand(NetworkDBQuery::FIND_NETWORK_LINK_ID, connection(), "@site_name", DBValue::DBTEXT); + myDeleteNetworkLinkUrls = SQLiteFactory::createCommand(NetworkDBQuery::DELETE_NETWORK_LINKURLS, connection(), "@link_id", DBValue::DBINT); + myDeleteNetworkLink = SQLiteFactory::createCommand(NetworkDBQuery::DELETE_NETWORK_LINK, connection(), "@link_id", DBValue::DBINT); + + mySaveNetworkLink = new SaveNetworkLinkRunnable(connection()); +} + +bool NetworkDB::clearDatabase() { + if (!isInitialized()) { + return false; + } + shared_ptr runnable = new ClearNetworkDBRunnable(connection()); + return executeAsTransaction(*runnable); +} + + +bool NetworkDB::saveNetworkLink(shared_ptr link) { + if (!isInitialized()) { + return false; + } + mySaveNetworkLink->setNetworkLink(link); + bool result = executeAsTransaction(*mySaveNetworkLink); + return result; +} + +bool NetworkDB::loadNetworkLinks(std::vector >& links) { + shared_ptr reader = myLoadNetworkLinks->executeReader(); + + links.clear(); + + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT) {/* link_id */ + return false; + } + std::map linkUrls; + ((DBIntValue &) *myLoadNetworkLinkUrls->parameter("@link_id").value()) = reader->intValue(0); + shared_ptr urlreader = myLoadNetworkLinkUrls->executeReader(); + long t = 0; + while (urlreader->next()) { + linkUrls[urlreader->textValue(0, std::string())] = urlreader->textValue(1, std::string()); + t = urlreader->intValue(2); + } + shared_ptr atomUpdated = new ATOMUpdated(); + atomUpdated->setLongSeconds_stupid(t); + std::string iconUrl; + if (linkUrls .count("icon") != 0) { + iconUrl = linkUrls["icon"]; + linkUrls.erase("icon"); + } + std::string siteName = reader->textValue(2, std::string()); + std::string predId = reader->textValue(5, std::string()); + std::string title = reader->textValue(1, std::string()); + std::string summary = reader->textValue(3, std::string()); + std::string language = reader->textValue(4, std::string()); + bool isEnabled = reader->intValue(6) == 1; + + shared_ptr link = new OPDSLink(siteName); + link->setTitle(title); + link->setSummary(summary); + link->setLanguage(language); + link->setIcon(iconUrl); + link->setLinks(linkUrls); + link->setPredefinedId(predId); + link->setEnabled(isEnabled); + link->setUpdated(atomUpdated); + links.push_back(link); + } + return true; +} + +bool NetworkDB::deleteNetworkLink(const std::string &siteName){ + ((DBTextValue &) *myFindNetworkLinkId->parameter("@site_name").value()) = siteName; + shared_ptr reader = myFindNetworkLinkId->executeReader(); + if (reader.isNull() || !reader->next()) { + return false; + } + int linkId = reader->intValue(0); + ((DBIntValue &) *myDeleteNetworkLink->parameter("@link_id").value()) = linkId; + ((DBIntValue &) *myDeleteNetworkLinkUrls->parameter("@link_id").value()) = linkId; + return myDeleteNetworkLinkUrls->execute() && myDeleteNetworkLink->execute(); + +} diff --git a/reader/src/database/networkdb/NetworkDB.h b/reader/src/database/networkdb/NetworkDB.h new file mode 100644 index 0000000..d4761a6 --- /dev/null +++ b/reader/src/database/networkdb/NetworkDB.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __NETWORKDB_H__ +#define __NETWORKDB_H__ + +#include +#include +#include + +#include "../sqldb/implsqlite/SQLiteDataBase.h" +#include "DBRunnables.h" + +class NetworkDB : public SQLiteDataBase { + +public: + static const std::string DATABASE_NAME; + + static NetworkDB &Instance(); + +private: + static shared_ptr ourInstance; + + NetworkDB(const std::string &path); + +public: + virtual ~NetworkDB(); + +public: + bool initDatabase(); + bool isInitialized() const; + bool clearDatabase(); + + bool saveNetworkLink(shared_ptr link); + bool loadNetworkLinks(std::vector >& links); + bool deleteNetworkLink(const std::string &siteName); + +private: + void initCommands(); + +private: + bool myInitialized; + + shared_ptr myLoadNetworkLinks; + shared_ptr myFindNetworkLinkId; + shared_ptr myDeleteNetworkLink; + shared_ptr myDeleteNetworkLinkUrls; + shared_ptr myLoadNetworkLinkUrls; + + shared_ptr mySaveNetworkLink; + +private: // disable copying + NetworkDB(const NetworkDB &); + const NetworkDB &operator = (const NetworkDB &); +}; + + +inline bool NetworkDB::isInitialized() const { return myInitialized; } + +#endif /* __NETWORKDB_H__ */ diff --git a/reader/src/database/networkdb/NetworkDBQuery.cpp b/reader/src/database/networkdb/NetworkDBQuery.cpp new file mode 100644 index 0000000..a8771cb --- /dev/null +++ b/reader/src/database/networkdb/NetworkDBQuery.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2004-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 "NetworkDBQuery.h" + +const std::string NetworkDBQuery::INIT_DATABASE = \ + " CREATE TABLE IF NOT EXISTS Links( " \ + " link_id INTEGER PRIMARY KEY, " \ + " title TEXT NOT NULL, " \ + " site_name TEXT NOT NULL, " \ + " summary TEXT, " \ + " language TEXT, " \ + " predefined_id TEXT, " \ + " is_enabled INTEGER " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS LinkUrls( " \ + " key TEXT NOT NULL, " \ + " link_id INTEGER NOT NULL REFERENCES Links(link_id), " \ + " url TEXT, " \ + " update_time INTEGER, " \ + " CONSTRAINT LinkUrls_PK PRIMARY KEY (key, link_id) " \ + "); "; + +const std::string NetworkDBQuery::CLEAR_DATABASE = \ + "DROP TABLE Links; " \ + "DROP TABLE LinkUrls; "; + + +const std::string NetworkDBQuery::FIND_NETWORK_LINK_ID = "SELECT link_id, predefined_id FROM Links WHERE site_name = @site_name; "; +const std::string NetworkDBQuery::ADD_NETWORK_LINK = \ + "INSERT INTO Links (title, site_name, summary, language, predefined_id, is_enabled) " \ + " VALUES ( " \ + " @title, " \ + " @site_name, " \ + " @summary, " \ + " nullif(@language,\"\"), " \ + " nullif(@predefined_id,\"\"), " \ + " @is_enabled " \ + " ); " \ + " " \ + "SELECT last_insert_rowid() AS link_id; "; + +const std::string NetworkDBQuery::DELETE_NETWORK_LINK = \ + "DELETE FROM Links WHERE link_id = @link_id; "; + +const std::string NetworkDBQuery::UPDATE_NETWORK_LINK = \ + "UPDATE Links SET " \ + " title = @title, " \ + " summary = @summary, " \ + " language = nullif(@language,\"\"), " \ + " predefined_id = nullif(@predefined_id,\"\"), " \ + " is_enabled = @is_enabled " \ + "WHERE " \ + " link_id = @link_id; "; + +const std::string NetworkDBQuery::ADD_NETWORK_LINKURL = \ + "INSERT INTO LinkUrls (key, link_id, url, update_time) " \ + " VALUES ( " \ + " @key, " \ + " @link_id, " \ + " @url, " \ + " @update_time " \ + " ); "; + +const std::string NetworkDBQuery::FIND_NETWORK_LINKURLS = "SELECT key, url, update_time FROM LinkUrls WHERE link_id = @link_id; "; + +const std::string NetworkDBQuery::UPDATE_NETWORK_LINKURL = \ + "UPDATE LinkUrls SET " \ + " url = @url, " \ + " update_time = @update_time " \ + "WHERE " \ + " link_id = @link_id AND key = @key; "; + +const std::string NetworkDBQuery::DELETE_NETWORK_LINKURLS = \ + "DELETE FROM LinkUrls " \ + "WHERE " \ + " link_id = @link_id; "; + +const std::string NetworkDBQuery::DELETE_NETWORK_LINKURL = \ + "DELETE FROM LinkUrls " \ + "WHERE " \ + " link_id = @link_id AND key = @key; "; + +const std::string NetworkDBQuery::LOAD_NETWORK_LINKS = "SELECT link_id, title, site_name, summary, language, predefined_id, is_enabled FROM Links; "; + +const std::string NetworkDBQuery::LOAD_NETWORK_LINKURLS = "SELECT key, url, update_time FROM LinkUrls WHERE link_id = @link_id; "; diff --git a/reader/src/database/networkdb/NetworkDBQuery.h b/reader/src/database/networkdb/NetworkDBQuery.h new file mode 100644 index 0000000..931705e --- /dev/null +++ b/reader/src/database/networkdb/NetworkDBQuery.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __NETWORKDBQUERY_H__ +#define __NETWORKDBQUERY_H__ + +#include + +class NetworkDBQuery { + +public: + static const std::string INIT_DATABASE; + static const std::string CLEAR_DATABASE; + + static const std::string FIND_NETWORK_LINK_ID; + static const std::string ADD_NETWORK_LINK; + static const std::string UPDATE_NETWORK_LINK; + static const std::string DELETE_NETWORK_LINK; + + static const std::string ADD_NETWORK_LINKURL; + static const std::string DELETE_NETWORK_LINKURLS; + static const std::string FIND_NETWORK_LINKURLS; + static const std::string DELETE_NETWORK_LINKURL; + static const std::string UPDATE_NETWORK_LINKURL; + + static const std::string LOAD_NETWORK_LINKS; + static const std::string LOAD_NETWORK_LINKURLS; + +private: // disable creation Instances + NetworkDBQuery(); +}; +#endif /* __NETWORKDBQUERY_H__ */ diff --git a/reader/src/database/networkdb/runnables/ClearNetworkDBRunnable.cpp b/reader/src/database/networkdb/runnables/ClearNetworkDBRunnable.cpp new file mode 100644 index 0000000..fe79ed3 --- /dev/null +++ b/reader/src/database/networkdb/runnables/ClearNetworkDBRunnable.cpp @@ -0,0 +1,39 @@ +/* + * 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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool ClearNetworkDBRunnable::run() { + shared_ptr cmd; + + cmd = SQLiteFactory::createCommand(NetworkDBQuery::CLEAR_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + cmd = SQLiteFactory::createCommand(NetworkDBQuery::INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + return true; +} + + diff --git a/reader/src/database/networkdb/runnables/InitNetworkDBRunnable.cpp b/reader/src/database/networkdb/runnables/InitNetworkDBRunnable.cpp new file mode 100644 index 0000000..d0730b7 --- /dev/null +++ b/reader/src/database/networkdb/runnables/InitNetworkDBRunnable.cpp @@ -0,0 +1,30 @@ +/* + * 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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool InitNetworkDBRunnable::run() { + shared_ptr cmd; + cmd = SQLiteFactory::createCommand(NetworkDBQuery::INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + return true; +} diff --git a/reader/src/database/networkdb/runnables/SaveNetworkLinkRunnable.cpp b/reader/src/database/networkdb/runnables/SaveNetworkLinkRunnable.cpp new file mode 100644 index 0000000..4c80499 --- /dev/null +++ b/reader/src/database/networkdb/runnables/SaveNetworkLinkRunnable.cpp @@ -0,0 +1,138 @@ +/* + * 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 "../DBRunnables.h" +#include "../../../network/NetworkLink.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveNetworkLinkRunnable::SaveNetworkLinkRunnable(DBConnection &connection) { + myFindNetworkLinkId = SQLiteFactory::createCommand(NetworkDBQuery::FIND_NETWORK_LINK_ID, connection, "@site_name", DBValue::DBTEXT); + myAddNetworkLink = SQLiteFactory::createCommand(NetworkDBQuery::ADD_NETWORK_LINK, connection, "@title", DBValue::DBTEXT, "@site_name", DBValue::DBTEXT, "@summary", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@predefined_id", DBValue::DBTEXT, "@is_enabled", DBValue::DBINT); + myUpdateNetworkLink = SQLiteFactory::createCommand(NetworkDBQuery::UPDATE_NETWORK_LINK, connection, "@title", DBValue::DBTEXT, "@summary", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@predefined_id", DBValue::DBTEXT, "@is_enabled", DBValue::DBINT, "@link_id", DBValue::DBINT); + + myFindNetworkLinkUrls = SQLiteFactory::createCommand(NetworkDBQuery::FIND_NETWORK_LINKURLS, connection, "@link_id", DBValue::DBINT); + myAddNetworkLinkUrl = SQLiteFactory::createCommand(NetworkDBQuery::ADD_NETWORK_LINKURL, connection, "@key", DBValue::DBTEXT, "@link_id", DBValue::DBINT, "@url", DBValue::DBTEXT, "@update_time", DBValue::DBINT); + myUpdateNetworkLinkUrl = SQLiteFactory::createCommand(NetworkDBQuery::UPDATE_NETWORK_LINKURL, connection, "@key", DBValue::DBTEXT, "@link_id", DBValue::DBINT, "@url", DBValue::DBTEXT, "@update_time", DBValue::DBINT); + myDeleteNetworkLinkUrl = SQLiteFactory::createCommand(NetworkDBQuery::DELETE_NETWORK_LINKURL, connection, "@key", DBValue::DBTEXT, "@link_id", DBValue::DBINT); +} + +bool SaveNetworkLinkRunnable::run() { + if (myNetworkLink.isNull()) { + return false; + } + ((DBTextValue &) *myFindNetworkLinkId->parameter("@site_name").value()) = myNetworkLink->getSiteName(); + shared_ptr reader = myFindNetworkLinkId->executeReader(); + if (reader.isNull() || !reader->next()) { + return addNetworkLink(); + } else if (myNetworkLink->isPredefined()) { + return updateNetworkLink(reader->intValue(0)) && updateNetworkLinkUrls(reader->intValue(0)); + } else { + //TODO implement for custom links + return false; + } + return false; +} + +bool SaveNetworkLinkRunnable::addNetworkLink() { + ((DBTextValue &) *myAddNetworkLink->parameter("@title").value()) = myNetworkLink->getTitle(); + ((DBTextValue &) *myAddNetworkLink->parameter("@site_name").value()) = myNetworkLink->getSiteName(); + ((DBTextValue &) *myAddNetworkLink->parameter("@summary").value()) = myNetworkLink->getSummary(); + ((DBTextValue &) *myAddNetworkLink->parameter("@language").value()) = myNetworkLink->getLanguage(); + ((DBTextValue &) *myAddNetworkLink->parameter("@predefined_id").value()) = myNetworkLink->getPredefinedId(); + ((DBIntValue &) *myAddNetworkLink->parameter("@is_enabled").value()) = myNetworkLink->isEnabled(); + shared_ptr dbLinkId = myAddNetworkLink->executeScalar(); + if (dbLinkId.isNull() || dbLinkId->type() != DBValue::DBINT || ((DBIntValue &) *dbLinkId).value() == 0) { + return false; + } + + bool allExecuted = true; + std::map tempLinks = myNetworkLink->getLinks(); + if (myNetworkLink->getIcon() != std::string()) { + tempLinks["icon"] = myNetworkLink->getIcon(); + } + long t = 0; + if (myNetworkLink->getUpdated() != 0) { + t = myNetworkLink->getUpdated()->getLongSeconds_stupid(); + } + for (std::map::iterator it = tempLinks.begin(); it != tempLinks.end(); ++it) { + ((DBTextValue &) *myAddNetworkLinkUrl->parameter("@key").value()) = it->first; + ((DBTextValue &) *myAddNetworkLinkUrl->parameter("@url").value()) = it->second; + ((DBIntValue &) *myAddNetworkLinkUrl->parameter("@link_id").value()) = ((DBIntValue &) *dbLinkId).value(); + ((DBIntValue &) *myAddNetworkLinkUrl->parameter("@update_time").value()) = t; + allExecuted = allExecuted && myAddNetworkLinkUrl->execute(); + } + return allExecuted; +} + +bool SaveNetworkLinkRunnable::updateNetworkLink(int linkId) { + ((DBTextValue &) *myUpdateNetworkLink->parameter("@title").value()) = myNetworkLink->getTitle(); + ((DBTextValue &) *myUpdateNetworkLink->parameter("@summary").value()) = myNetworkLink->getSummary(); + ((DBTextValue &) *myUpdateNetworkLink->parameter("@language").value()) = myNetworkLink->getLanguage(); + ((DBTextValue &) *myUpdateNetworkLink->parameter("@predefined_id").value()) = myNetworkLink->getPredefinedId(); + ((DBIntValue &) *myUpdateNetworkLink->parameter("@is_enabled").value()) = myNetworkLink->isEnabled(); + ((DBIntValue &) *myUpdateNetworkLink->parameter("@link_id").value()) = linkId; + + return myUpdateNetworkLink->execute(); +} + +bool SaveNetworkLinkRunnable::updateNetworkLinkUrls(int linkId) { + bool allExecuted = true; + ((DBIntValue &) *myFindNetworkLinkUrls->parameter("@link_id").value()) = linkId; + shared_ptr reader = myFindNetworkLinkUrls->executeReader(); + std::map linksToCheck = myNetworkLink->getLinks(); + if (!myNetworkLink->getIcon().empty()) { + linksToCheck["icon"] = myNetworkLink->getIcon(); + } + long t = 0; + if (!myNetworkLink->getUpdated().isNull()) { + t = myNetworkLink->getUpdated()->getLongSeconds_stupid(); + } + while (reader->next()) { + if (reader->type(0) != DBValue::DBTEXT || reader->type(1) != DBValue::DBTEXT) { + return false; + } + std::string key = reader->textValue(0, std::string()); +// std::string url = reader->textValue(1, std::string()); + if (linksToCheck.count(key) == 0) { + ((DBTextValue &) *myDeleteNetworkLinkUrl->parameter("@key").value()) = key; + ((DBIntValue &) *myDeleteNetworkLinkUrl->parameter("@link_id").value()) = linkId; + allExecuted = allExecuted && myDeleteNetworkLinkUrl->execute(); + } else { + ((DBTextValue &) *myUpdateNetworkLinkUrl->parameter("@key").value()) = key; + ((DBTextValue &) *myUpdateNetworkLinkUrl->parameter("@url").value()) = linksToCheck[key]; + ((DBIntValue &) *myUpdateNetworkLinkUrl->parameter("@link_id").value()) = linkId; + ((DBIntValue &) *myUpdateNetworkLinkUrl->parameter("@update_time").value()) = t; + linksToCheck.erase(key); + allExecuted = allExecuted && myUpdateNetworkLinkUrl->execute(); + } + } + + for (std::map::iterator it = linksToCheck.begin(); it != linksToCheck.end(); ++it) { + ((DBTextValue &) *myAddNetworkLinkUrl->parameter("@key").value()) = it->first; + ((DBTextValue &) *myAddNetworkLinkUrl->parameter("@url").value()) = it->second; + ((DBIntValue &) *myAddNetworkLinkUrl->parameter("@link_id").value()) = linkId; + ((DBIntValue &) *myAddNetworkLinkUrl->parameter("@update_time").value()) = t; + allExecuted = allExecuted && myAddNetworkLinkUrl->execute(); + } + return allExecuted; +} + +void SaveNetworkLinkRunnable::setNetworkLink(shared_ptr link) { + myNetworkLink = link; +} diff --git a/reader/src/database/sqldb/DBCommand.cpp b/reader/src/database/sqldb/DBCommand.cpp new file mode 100644 index 0000000..8986401 --- /dev/null +++ b/reader/src/database/sqldb/DBCommand.cpp @@ -0,0 +1,59 @@ +/* + * 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 "DBCommand.h" +#include + +DBCommand::DBCommand(const std::string &command, DBConnection &connection) + : myParameters() + , myCommandString(command) + , myConnection(connection) { +} + +DBCommand::~DBCommand() { +} + + +class ParameterPredicate { + +public: + ParameterPredicate(const std::string &name); + bool operator () (const DBCommandParameter &p); +private: + const std::string &myName; +}; + +ParameterPredicate::ParameterPredicate(const std::string &name) : myName(name) {} + +bool ParameterPredicate::operator () (const DBCommandParameter &p) { + return p.name() == myName; +} + + +DBCommandParameter &DBCommand::parameter(const std::string &name) { + std::vector::iterator it = std::find_if(myParameters.begin(), myParameters.end(), ParameterPredicate(name)); + return *it; +} + +const DBCommandParameter &DBCommand::parameters(const std::string &name) const { + std::vector::const_iterator it = std::find_if(myParameters.begin(), myParameters.end(), ParameterPredicate(name)); + return *it; +} + diff --git a/reader/src/database/sqldb/DBCommand.h b/reader/src/database/sqldb/DBCommand.h new file mode 100644 index 0000000..2ac9ade --- /dev/null +++ b/reader/src/database/sqldb/DBCommand.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + + +#ifndef __DBCOMMAND_H__ +#define __DBCOMMAND_H__ + +#include "DBCommandParameter.h" +#include "DBDataReader.h" +#include "DBConnection.h" + +class DBCommand { + +public: + DBCommand(const std::string &command, DBConnection &connection); + virtual ~DBCommand(); + + std::vector ¶meters(); + const std::vector ¶meters() const; + + DBCommandParameter ¶meter(const std::string &name); + const DBCommandParameter ¶meters(const std::string &name) const; + + const std::string &commandString() const; + +protected: + const DBConnection &connection() const; + +public: // to implement: + virtual bool execute() = 0; + virtual shared_ptr executeScalar() = 0; + virtual shared_ptr executeReader() = 0; + +private: // against copying: + DBCommand(const DBCommand &); + const DBCommand &operator = (const DBCommand &); + +private: + std::vector myParameters; + const std::string myCommandString; + const DBConnection &myConnection; +}; + + +inline std::vector &DBCommand::parameters() { return myParameters; } +inline const std::vector &DBCommand::parameters() const { return myParameters; } + +inline const std::string &DBCommand::commandString() const { return myCommandString; } +inline const DBConnection &DBCommand::connection() const { return myConnection; } + + +#endif /* __DBCOMMAND_H__ */ + diff --git a/reader/src/database/sqldb/DBCommandParameter.cpp b/reader/src/database/sqldb/DBCommandParameter.cpp new file mode 100644 index 0000000..d8ec08a --- /dev/null +++ b/reader/src/database/sqldb/DBCommandParameter.cpp @@ -0,0 +1,63 @@ +/* + * 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 "DBCommandParameter.h" + + +DBCommandParameter::DBCommandParameter() + : myName(""), myValue(DBValue::create(DBValue::DBNULL)) { +} + +DBCommandParameter::DBCommandParameter(const std::string &name) + : myName(name), myValue(DBValue::create(DBValue::DBNULL)) { +} + +DBCommandParameter::DBCommandParameter(DBValue::ValueType type) + : myName(""), myValue(DBValue::create(type)) { +} + +DBCommandParameter::DBCommandParameter(const std::string &name, DBValue::ValueType type) + : myName(name), myValue(DBValue::create(type)) { +} + +DBCommandParameter::DBCommandParameter(shared_ptr value) + : myName(""), myValue(value) { + if (value.isNull()) { + myValue = DBValue::create(DBValue::DBNULL); + } +} + +DBCommandParameter::DBCommandParameter(const std::string &name, shared_ptr value) + : myName(name), myValue(value) { + if (value.isNull()) { + myValue = DBValue::create(DBValue::DBNULL); + } +} + +DBCommandParameter::DBCommandParameter(const DBCommandParameter ¶meter) + : myName(parameter.name()), myValue(parameter.value()) { +} + +const DBCommandParameter &DBCommandParameter::operator = (const DBCommandParameter ¶meter) { + myName = parameter.name(); + myValue = parameter.value(); + return *this; +} + diff --git a/reader/src/database/sqldb/DBCommandParameter.h b/reader/src/database/sqldb/DBCommandParameter.h new file mode 100644 index 0000000..2e3c8ae --- /dev/null +++ b/reader/src/database/sqldb/DBCommandParameter.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + + +#ifndef __DBCOMMANDPARAMETERS_H__ +#define __DBCOMMANDPARAMETERS_H__ + +#include + +#include "DBValues.h" + + +class DBCommandParameter { + +public: + // create anonymous parameter with DBNULL value: + DBCommandParameter(); + + // create named parameter with DBNULL value: + DBCommandParameter(const std::string &name); + + // create anonymous parameter with default value of specified type: + DBCommandParameter(DBValue::ValueType type); + + // create named parameter with default value of specified type: + DBCommandParameter(const std::string &name, DBValue::ValueType type); + + // create anonymous parameter with specified value: + DBCommandParameter(shared_ptr value); + + // create named parameter with specified value: + DBCommandParameter(const std::string &name, shared_ptr value); + +public: + const std::string &name() const; + void setName(const std::string &name); + + shared_ptr value() const; + void setValue(shared_ptr value); + + DBValue::ValueType type() const; + + bool hasName() const; + +public: // implement copying: + DBCommandParameter(const DBCommandParameter &par); + const DBCommandParameter &operator = (const DBCommandParameter &par); + +private: + std::string myName; + DBValue::ValueType myType; + shared_ptr myValue; +}; + + +inline const std::string &DBCommandParameter::name() const { return myName; } +inline void DBCommandParameter::setName(const std::string &name) { myName = name; } +inline shared_ptr DBCommandParameter::value() const { return myValue; } +inline void DBCommandParameter::setValue(shared_ptr value) { myValue = value; } +inline DBValue::ValueType DBCommandParameter::type() const { return myValue->type(); } +inline bool DBCommandParameter::hasName() const { return myName != ""; } + + +#endif /* __DBCOMMANDPARAMETERS_H__ */ + diff --git a/reader/src/database/sqldb/DBConnection.cpp b/reader/src/database/sqldb/DBConnection.cpp new file mode 100644 index 0000000..20f4402 --- /dev/null +++ b/reader/src/database/sqldb/DBConnection.cpp @@ -0,0 +1,28 @@ +/* + * 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 "DBConnection.h" + +DBConnection::DBConnection() { +} + +DBConnection::~DBConnection() { +} + diff --git a/reader/src/database/sqldb/DBConnection.h b/reader/src/database/sqldb/DBConnection.h new file mode 100644 index 0000000..2345ddf --- /dev/null +++ b/reader/src/database/sqldb/DBConnection.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + + +#ifndef __DBCONNECTION_H__ +#define __DBCONNECTION_H__ + +class DBConnection { +public: + DBConnection(); + virtual ~DBConnection(); + +public: // to implement: + virtual bool open() = 0; + virtual bool close() = 0; + virtual bool isOpened() const = 0; + +private: // against copying: + DBConnection(const DBConnection &); + const DBConnection &operator = (const DBConnection &); +}; + +#endif /* __DBCONNECTION_H__ */ + diff --git a/reader/src/database/sqldb/DBDataReader.cpp b/reader/src/database/sqldb/DBDataReader.cpp new file mode 100644 index 0000000..7c40b03 --- /dev/null +++ b/reader/src/database/sqldb/DBDataReader.cpp @@ -0,0 +1,26 @@ +/* + * 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 "DBDataReader.h" + +DBDataReader::DBDataReader() { +} + +DBDataReader::~DBDataReader() { +} diff --git a/reader/src/database/sqldb/DBDataReader.h b/reader/src/database/sqldb/DBDataReader.h new file mode 100644 index 0000000..f07b323 --- /dev/null +++ b/reader/src/database/sqldb/DBDataReader.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef __DBDATAREADER_H__ +#define __DBDATAREADER_H__ + +#include "DBValues.h" + +class DBDataReader { + +public: + DBDataReader(); + virtual ~DBDataReader(); + +public: // to implement: + virtual bool next() = 0; + virtual bool reset() = 0; + virtual void close() = 0; + + virtual std::size_t columnsNumber() const = 0; + + virtual DBValue::ValueType type(std::size_t column) const = 0; + virtual shared_ptr value(std::size_t column) const = 0; + + virtual int intValue(std::size_t column) const = 0; + virtual double realValue(std::size_t column) const = 0; + virtual std::string textValue(std::size_t column, const std::string &defaultValue) const = 0; + +public: + bool isDBNull(std::size_t column) const; + bool isInt(std::size_t column) const; + bool isReal(std::size_t column) const; + bool isText(std::size_t column) const; +}; + +inline bool DBDataReader::isDBNull(std::size_t column) const { return type(column) == DBValue::DBNULL; } +inline bool DBDataReader::isInt(std::size_t column) const { return type(column) == DBValue::DBINT; } +inline bool DBDataReader::isReal(std::size_t column) const { return type(column) == DBValue::DBREAL; } +inline bool DBDataReader::isText(std::size_t column) const { return type(column) == DBValue::DBTEXT; } + +#endif /* __DBDATAREADER_H__ */ + diff --git a/reader/src/database/sqldb/DBIntValue.cpp b/reader/src/database/sqldb/DBIntValue.cpp new file mode 100644 index 0000000..cd3e900 --- /dev/null +++ b/reader/src/database/sqldb/DBIntValue.cpp @@ -0,0 +1,37 @@ +/* + * 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 "DBValues.h" + + +DBIntValue::DBIntValue() + : myValue(0) { +} + +DBIntValue::DBIntValue(int value) + : myValue(value) { +} + +DBIntValue::~DBIntValue() { +} + +DBValue::ValueType DBIntValue::type() const { + return DBINT; +} + diff --git a/reader/src/database/sqldb/DBNullValue.cpp b/reader/src/database/sqldb/DBNullValue.cpp new file mode 100644 index 0000000..cd6ab1c --- /dev/null +++ b/reader/src/database/sqldb/DBNullValue.cpp @@ -0,0 +1,34 @@ +/* + * 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 "DBValues.h" + +const shared_ptr DBNullValue::Instance = new DBNullValue(); + + +DBNullValue::DBNullValue() { +} + +DBNullValue::~DBNullValue() { +} + +DBValue::ValueType DBNullValue::type() const { + return DBNULL; +} + diff --git a/reader/src/database/sqldb/DBRealValue.cpp b/reader/src/database/sqldb/DBRealValue.cpp new file mode 100644 index 0000000..eb849e5 --- /dev/null +++ b/reader/src/database/sqldb/DBRealValue.cpp @@ -0,0 +1,37 @@ +/* + * 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 "DBValues.h" + + +DBRealValue::DBRealValue() + : myValue(0.0) { +} + +DBRealValue::DBRealValue(double value) + : myValue(value) { +} + +DBRealValue::~DBRealValue() { +} + +DBValue::ValueType DBRealValue::type() const { + return DBREAL; +} + diff --git a/reader/src/database/sqldb/DBRunnable.h b/reader/src/database/sqldb/DBRunnable.h new file mode 100644 index 0000000..8e44a67 --- /dev/null +++ b/reader/src/database/sqldb/DBRunnable.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __DBRUNNABLE_H__ +#define __DBRUNNABLE_H__ + + +class DBRunnable { + +public: + virtual ~DBRunnable() {} + + virtual bool run() = 0; +}; + +#endif /* __DBRUNNABLE_H__ */ diff --git a/reader/src/database/sqldb/DBTextValue.cpp b/reader/src/database/sqldb/DBTextValue.cpp new file mode 100644 index 0000000..27b4d96 --- /dev/null +++ b/reader/src/database/sqldb/DBTextValue.cpp @@ -0,0 +1,48 @@ +/* + * 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 "DBValues.h" + + +DBTextValue::DBTextValue() + : myValue("") { +} + +DBTextValue::DBTextValue(const std::string &value) + : myValue(value) { +} + +DBTextValue::~DBTextValue() { +} + +DBValue::ValueType DBTextValue::type() const { + return DBTEXT; +} + + +DBTextValue::DBTextValue(const DBTextValue &value) + : DBValue() + , myValue(value.myValue) { +} + +const DBTextValue &DBTextValue::operator = (const DBTextValue &value) { + myValue = value.myValue; + return *this; +} + diff --git a/reader/src/database/sqldb/DBValue.cpp b/reader/src/database/sqldb/DBValue.cpp new file mode 100644 index 0000000..75c4376 --- /dev/null +++ b/reader/src/database/sqldb/DBValue.cpp @@ -0,0 +1,42 @@ +/* + * 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 "DBValues.h" + + +DBValue::DBValue() { +} + +DBValue::~DBValue() { +} + +shared_ptr DBValue::create(ValueType type) { + switch (type) { + case DBNULL: + return DBNullValue::Instance; + case DBINT: + return new DBIntValue(); + case DBREAL: + return new DBRealValue(); + case DBTEXT: + return new DBTextValue(); + } + return 0; +} + diff --git a/reader/src/database/sqldb/DBValues.h b/reader/src/database/sqldb/DBValues.h new file mode 100644 index 0000000..eb64d56 --- /dev/null +++ b/reader/src/database/sqldb/DBValues.h @@ -0,0 +1,145 @@ +/* + * 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. + */ + + +#ifndef __DBVALUES_H__ +#define __DBVALUES_H__ + +#include +#include + + +class DBValue { + +public: + enum ValueType { + DBNULL, + DBINT, + DBREAL, + DBTEXT, + }; + + static shared_ptr create(ValueType type); + +public: + DBValue(); + virtual ~DBValue(); + +public: // to implement: + virtual ValueType type() const = 0; +}; + +class DBNullValue : public DBValue { + +friend class DBValue; + +public: + static const shared_ptr Instance; // the only Instance of DBNullValue class + +private: + DBNullValue(); + ~DBNullValue(); + +public: + ValueType type() const; +}; + +class DBIntValue : public DBValue { + +friend class DBValue; + +protected: + DBIntValue(); + +public: + DBIntValue(int value); + ~DBIntValue(); + + ValueType type() const; + + int value() const; + void setValue(int value); + const DBIntValue &operator = (int value); + +private: + int myValue; +}; + +class DBRealValue : public DBValue { + +friend class DBValue; + +protected: + DBRealValue(); + +public: + DBRealValue(double value); + ~DBRealValue(); + + ValueType type() const; + + double value() const; + void setValue(double value); + const DBRealValue &operator = (double value); + +private: + double myValue; +}; + +class DBTextValue : public DBValue { + +friend class DBValue; + +protected: + DBTextValue(); + +public: + DBTextValue(const std::string &value); + ~DBTextValue(); + + ValueType type() const; + + const std::string &value() const; + void setValue(const std::string &value); + const DBTextValue &operator = (const std::string &value); + +public: + DBTextValue(const DBTextValue &value); + const DBTextValue &operator = (const DBTextValue &value); + +private: + std::string myValue; +}; + + + +inline int DBIntValue::value() const { return myValue; } +inline void DBIntValue::setValue(int value) { myValue = value; } +inline const DBIntValue &DBIntValue::operator = (int value) { myValue = value; return *this; } + +inline double DBRealValue::value() const { return myValue; } +inline void DBRealValue::setValue(double value) { myValue = value; } +inline const DBRealValue &DBRealValue::operator = (double value) { myValue = value; return *this; } + +inline const std::string &DBTextValue::value() const { return myValue; } +inline void DBTextValue::setValue(const std::string &value) { myValue = value; } +inline const DBTextValue &DBTextValue::operator = (const std::string &value) { myValue = value; return *this; } + +#endif /* __DBVALUES_H__ */ + diff --git a/reader/src/database/sqldb/DataBase.cpp b/reader/src/database/sqldb/DataBase.cpp new file mode 100644 index 0000000..e1d0b3f --- /dev/null +++ b/reader/src/database/sqldb/DataBase.cpp @@ -0,0 +1,31 @@ +/* + * 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 "DataBase.h" + +std::string DataBase::databaseDirName() { + return ZLibrary::ApplicationWritableDirectory(); +} + +DataBase::~DataBase() { +} + + diff --git a/reader/src/database/sqldb/DataBase.h b/reader/src/database/sqldb/DataBase.h new file mode 100644 index 0000000..33430aa --- /dev/null +++ b/reader/src/database/sqldb/DataBase.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + + +#ifndef __DATABASE_H__ +#define __DATABASE_H__ + +#include + +#include "DBConnection.h" +#include "DBRunnable.h" + +class DataBase { + +public: + static std::string databaseDirName(); + +public: + DataBase(shared_ptr connection); + virtual ~DataBase(); + + DBConnection &connection() const; + +protected: + virtual bool executeAsTransaction(DBRunnable &runnable) = 0; + +private: + shared_ptr myConnection; + +private: // against copying: + DataBase(const DataBase &); + const DataBase &operator = (const DataBase &); +}; + +inline DataBase::DataBase(shared_ptr connection) : myConnection(connection) { } + +inline DBConnection &DataBase::connection() const { return *myConnection; } + +#endif /* __DATABASE_H__ */ diff --git a/reader/src/database/sqldb/implsqlite/SQLiteCommand.cpp b/reader/src/database/sqldb/implsqlite/SQLiteCommand.cpp new file mode 100644 index 0000000..58d90f6 --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteCommand.cpp @@ -0,0 +1,366 @@ +/* + * 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 "SQLiteCommand.h" + +#include "SQLiteConnection.h" +#include "SQLiteDataReader.h" + + +std::string SQLiteCommand::packCommand(const std::string &command) { + static const char _spaces[] = " \t\n"; + std::string stripped = command; + ZLStringUtil::stripWhiteSpaces(stripped); + + std::size_t pos = 0; + while (true) { + pos = stripped.find_first_of(_spaces, pos); + if (pos == std::string::npos) { + break; + } + stripped[pos++] = ' '; + const std::size_t next = stripped.find_first_not_of(_spaces, pos); + if (pos != std::string::npos && next > pos) { + stripped.erase(pos, next - pos); + } + } + return stripped; +} + + +SQLiteCommand::~SQLiteCommand() { + SQLiteConnection &con = (SQLiteConnection &) connection(); + if (con.isOpened() && myStatements.size() != 0) { + finalizeStatements(); + } +} + + +bool SQLiteCommand::execute() { + ZLLogger::Instance().println("sqlite", "execute: " + commandString()); + + SQLiteConnection &con = (SQLiteConnection &) connection(); + if (!con.isOpened()) { + myStatements.clear(); + return false; + } + if (!prepareStatements(con)) { + return false; + } + std::vector::iterator it = myStatements.begin(); + std::vector::iterator end = myStatements.end(); + while (true) { + int res = sqlite3_step(*it); + switch (res) { + case SQLITE_DONE: + if (++it == end) { + resetStatements(); + return true; + } + break; + case SQLITE_OK: + case SQLITE_ROW: + break; + default: + dumpError(); + finalizeStatements(); + return false; + } + } +} + +shared_ptr SQLiteCommand::executeScalar() { + ZLLogger::Instance().println("sqlite", "executeScalar: " + commandString()); + + SQLiteConnection &con = (SQLiteConnection &) connection(); + if (!con.isOpened()) { + myStatements.clear(); + return 0; + } + if (!prepareStatements(con)) { + return 0; + } + std::vector::iterator it = myStatements.begin(); + std::vector::iterator end = myStatements.end(); + while (true) { + int res = sqlite3_step(*it); + switch (res) { + case SQLITE_DONE: + if (++it == end) { + resetStatements(); + return 0; + } + break; + case SQLITE_OK: + break; + case SQLITE_ROW: { + shared_ptr val = SQLiteDataReader::makeDBValue(*it, /* column = */ 0); + resetStatements(); + return val; + } + default: + dumpError(); + finalizeStatements(); + return 0; + } + } +} + +shared_ptr SQLiteCommand::executeReader() { + ZLLogger::Instance().println("sqlite", "executeReader: " + commandString()); + + SQLiteConnection &con = (SQLiteConnection &) connection(); + if (!con.isOpened()) { + myStatements.clear(); + return 0; + } + if (!prepareStatements(con)) { + return 0; + } + myLocked = true; + return new SQLiteDataReader(*this); +} + + +bool SQLiteCommand::prepareStatements(SQLiteConnection &conn) { + sqlite3 *db = conn.database(); + if (myLocked) { + return false; + } + if (myStatements.size() != 0) { + const std::size_t size = myStatements.size(); + int res = SQLITE_OK; + for (std::size_t i = 0; i < size && res == SQLITE_OK; ++i) { + res = sqlite3_reset(myStatements[i]); + } + if (res == SQLITE_OK) { + bindParameters(); + return true; + } + finalizeStatements(); + } + const std::string sql = commandString(); + const int length = -1; + const char *tail = sql.c_str(); + while (true) { + sqlite3_stmt *statement; + int res = sqlite3_prepare_v2(db, tail, length, &statement, &tail); + if (res != SQLITE_OK) { + dumpError(); + finalizeStatements(); + return false; + } + if (statement == 0) { + break; + } + myStatements.push_back(statement); + conn.addStatement(statement); + } + if (!bindParameters()) { + finalizeStatements(); + return false; + } + return true; +} + + +void SQLiteCommand::prepareBindContext() { + if (myBindContext.size() > 0) { + return; + } + + std::size_t number = 0; + + for (std::size_t i = 0; i < myStatements.size(); ++i) { + sqlite3_stmt *statement = myStatements[i]; + const int count = sqlite3_bind_parameter_count(statement); + for (int j = 1; j <= count; ++j) { + ++number; + const char *name = sqlite3_bind_parameter_name(statement, j); + if (name == 0) { + myBindContext.push_back(BindParameter(number)); + } else { + const std::string namestr(name); + if (std::find_if(myBindContext.begin(), myBindContext.end(), BindParameterComparator(namestr)) == myBindContext.end()) { + myBindContext.push_back(BindParameter(number, namestr)); + } + } + } + } +} + + +bool SQLiteCommand::bindParameters() { + prepareBindContext(); + + std::vector ¶ms = parameters(); + + bool res = true; + const std::size_t size = params.size(); + for (std::size_t i = 0; i < size; ++i) { + DBCommandParameter &p = params[i]; + if (p.hasName()) { + const std::string &name = p.name(); + if (!bindParameterByName(name, p.value())) { + res = false; + } + } else if (i < myBindContext.size()) { + BindParameter &bp = myBindContext[i]; + if (myBindContext[i].hasName()) { + if (!bindParameterByName(bp.Name, p.value())) { + res = false; + } + } else { + if (!bindParameterByIndex(bp.Position, p.value())) { + res = false; + } + } + } else { + res = false; + } + } + return res; +} + + +bool SQLiteCommand::bindParameterByName(const std::string &name, shared_ptr value) { + const std::size_t size = myStatements.size(); + bool res = true; + bool binded = false; + for (std::size_t i = 0; i < size; ++i) { + sqlite3_stmt *statement = myStatements[i]; + const int index = sqlite3_bind_parameter_index(statement, name.c_str()); + if (index == 0) { + continue; + } + binded = true; + if (!bindParameter(statement, index, value)) { + res = false; + } + } + if (!binded) { + dumpError("parameter \"" + name + "\" is not found"); + } + return res; +} + +bool SQLiteCommand::bindParameterByIndex(std::size_t index, shared_ptr value) { + if (index == 0) { + return true; + } + const std::size_t size = myStatements.size(); + int number = index; + for (std::size_t i = 0; i < size; ++i) { + sqlite3_stmt *statement = myStatements[i]; + const int count = sqlite3_bind_parameter_count(statement); + if (number > count) { + number -= count; + continue; + } + return bindParameter(statement, number, value); + } + return true; +} + +bool SQLiteCommand::bindParameter(sqlite3_stmt *statement, int number, shared_ptr value) { + DBValue::ValueType type = (value.isNull()) ? (DBValue::DBNULL) : (value->type()); + int res; + switch (type) { + case DBValue::DBNULL: + res = sqlite3_bind_null(statement, number); + break; + case DBValue::DBINT: + res = sqlite3_bind_int(statement, number, ((DBIntValue &) *value).value()); + break; + case DBValue::DBREAL: + res = sqlite3_bind_double(statement, number, ((DBRealValue &) *value).value()); + break; + case DBValue::DBTEXT: + res = sqlite3_bind_text(statement, number, ((DBTextValue &) *value).value().c_str(), -1 /* zero-terminated string */, SQLITE_TRANSIENT); + break; + default: + return false; + } + if (res != SQLITE_OK) { + dumpError(); + } + return res == SQLITE_OK; +} + + +void SQLiteCommand::finalizeStatements() { + SQLiteConnection &con = (SQLiteConnection &) connection(); + const std::size_t size = myStatements.size(); + for (std::size_t i = 0; i < size; ++i) { + sqlite3_stmt *statement = myStatements[i]; + con.removeStatement(statement); + const int res = sqlite3_finalize(statement); + if (res != SQLITE_OK) { + dumpError(); + } + } + myStatements.clear(); +} + + +void SQLiteCommand::dumpError() const { + static const std::size_t cmdlimit = 114; + ((SQLiteConnection &) connection()).dumpError(); + const std::string &cmd = commandString(); + if (cmd.length() > cmdlimit) { + std::cerr << "SQLITE IMPLEMENTATION ERROR: in command \"" << cmd.substr(0, cmdlimit - 3) << "...\"" << std::endl; + } else { + std::cerr << "SQLITE IMPLEMENTATION ERROR: in command \"" << cmd << "\"" << std::endl; + } +} + +void SQLiteCommand::dumpError(const std::string &msg) const { + static const std::size_t cmdlimit = 129; + std::cerr << "SQLITE ERROR: " << msg << std::endl; + const std::string &cmd = commandString(); + if (cmd.length() > cmdlimit) { + std::cerr << "SQLITE ERROR: in command \"" << cmd.substr(0, cmdlimit - 3) << "...\"" << std::endl; + } else { + std::cerr << "SQLITE ERROR: in command \"" << cmd << "\"" << std::endl; + } +} + +bool SQLiteCommand::resetStatements() { + if (myStatements.size() == 0) { + return true; + } + const std::size_t size = myStatements.size(); + int res = SQLITE_OK; + for (std::size_t i = 0; i < size && res == SQLITE_OK; ++i) { + res = sqlite3_reset(myStatements[i]); + } + if (res == SQLITE_OK) { + return true; + } + dumpError(); + finalizeStatements(); + return false; +} + diff --git a/reader/src/database/sqldb/implsqlite/SQLiteCommand.h b/reader/src/database/sqldb/implsqlite/SQLiteCommand.h new file mode 100644 index 0000000..395fd30 --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteCommand.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + + +#ifndef __SQLITECOMMAND_H__ +#define __SQLITECOMMAND_H__ + +#include + +#include "../DBCommand.h" + +#include "SQLiteConnection.h" + + +/* + * Command can contain parameters with following names: + * ? - anonymous parameter + * @AAA - named parameter + * + * where AAA is alpha-numeric parameter name + */ +class SQLiteCommand : public DBCommand { + +public: + static std::string packCommand(const std::string &command); + +public: + SQLiteCommand(const std::string &command, DBConnection &connection); + ~SQLiteCommand(); + + bool execute(); + shared_ptr executeScalar(); + shared_ptr executeReader(); + +public: + void unlock(); + + // TODO: hide sqlite3_stmt object inside + std::vector &statements(); + const std::vector &statements() const; + + void dumpError() const; + void dumpError(const std::string &msg) const; + + bool resetStatements(); + +private: + + struct BindParameter { + std::size_t Position; + std::string Name; + + BindParameter(std::size_t pos) : Position(pos), Name("") {} + BindParameter(std::size_t pos, const std::string &name) : Position(pos), Name(name) {} + + bool hasName() const; + }; + + class BindParameterComparator { + + public: + BindParameterComparator(const std::string &name); + bool operator () (const SQLiteCommand::BindParameter &p) const; + + private: + const std::string myName; + }; + + void prepareBindContext(); + bool bindParameters(); + bool bindParameterByName(const std::string &name, shared_ptr value); + bool bindParameterByIndex(std::size_t index, shared_ptr value); + bool bindParameter(sqlite3_stmt *statement, int number, shared_ptr value); + bool prepareStatements(SQLiteConnection &conn); + + void finalizeStatements(); + +private: + std::vector myStatements; + std::vector myBindContext; + bool myLocked; + +private: // disable copying: + SQLiteCommand(const SQLiteCommand &); + const SQLiteCommand &operator = (const SQLiteCommand &); +}; + + +inline SQLiteCommand::SQLiteCommand(const std::string &command, DBConnection &connection) + : DBCommand(SQLiteCommand::packCommand(command), connection), myStatements(), myLocked(false) {} + +inline void SQLiteCommand::unlock() { myLocked = false; } +inline std::vector &SQLiteCommand::statements() { return myStatements; } +inline const std::vector &SQLiteCommand::statements() const { return myStatements; } + +inline bool SQLiteCommand::BindParameter::hasName() const { return Name.size() > 0; } + +inline SQLiteCommand::BindParameterComparator::BindParameterComparator(const std::string &name) : myName(name) {} +inline bool SQLiteCommand::BindParameterComparator::operator () (const SQLiteCommand::BindParameter &p) const { return myName == p.Name; } + +#endif /* __SQLITECOMMAND_H__ */ diff --git a/reader/src/database/sqldb/implsqlite/SQLiteConnection.cpp b/reader/src/database/sqldb/implsqlite/SQLiteConnection.cpp new file mode 100644 index 0000000..f1a0f30 --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteConnection.cpp @@ -0,0 +1,86 @@ +/* + * 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 "SQLiteConnection.h" + +SQLiteConnection::SQLiteConnection(const std::string &name) + : DBConnection() + , myName(name) + , myDatabase(0) { +} + +SQLiteConnection::~SQLiteConnection() { + if (isOpened()) { + close(); + } +} + + +bool SQLiteConnection::open() { + if (myDatabase != 0) { + return true; + } + int res = sqlite3_open(myName.c_str(), &myDatabase); + if (res == SQLITE_OK) { + return true; + } + dumpError(); + if (myDatabase != 0) { + sqlite3_close(myDatabase); + myDatabase = 0; + } + return false; +} + +void SQLiteConnection::finalizeOpenedStatements() { + std::size_t size = myStatements.size(); + for (std::size_t i = 0; i < size; ++i) { + const int res = sqlite3_finalize(myStatements[i]); + if (res != SQLITE_OK) { + dumpError(); + } + } + myStatements.clear(); +} + +bool SQLiteConnection::close() { + if (myDatabase == 0) { + return true; + } + + finalizeOpenedStatements(); + + int res = sqlite3_close(myDatabase); + if (res == SQLITE_OK) { + myDatabase = 0; + return true; + } + dumpError(); + return false; +} + +void SQLiteConnection::dumpError() const { + if (myDatabase != 0) { + const std::string msg = sqlite3_errmsg(myDatabase); // TODO: error & message handling + const int code = sqlite3_errcode(myDatabase); // TODO: error & message handling + std::cerr << "SQLITE IMPLEMENTATION ERROR: (" << code << ") " << msg << std::endl; + } +} diff --git a/reader/src/database/sqldb/implsqlite/SQLiteConnection.h b/reader/src/database/sqldb/implsqlite/SQLiteConnection.h new file mode 100644 index 0000000..6854800 --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteConnection.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef __SQLITECONNECTION_H__ +#define __SQLITECONNECTION_H__ + +#include + +#include +#include +#include + +#include "../DBConnection.h" + +class SQLiteConnection : public DBConnection { + +public: + SQLiteConnection(const std::string &name); + ~SQLiteConnection(); + + bool open(); + bool close(); + bool isOpened() const; + +public: + const std::string &name() const; + + sqlite3 *database(); // TODO: hide sqlite3 object inside + + void addStatement(sqlite3_stmt *statement); + void removeStatement(sqlite3_stmt *statement); + +public: + void dumpError() const; + +private: + void finalizeOpenedStatements(); + +private: + const std::string myName; + sqlite3 *myDatabase; + std::vector myStatements; + +private: // disable copying: + SQLiteConnection(const SQLiteConnection &); + const SQLiteConnection &operator = (const SQLiteConnection &); +}; + + +inline const std::string &SQLiteConnection::name() const { return myName; } +inline sqlite3 *SQLiteConnection::database() { return myDatabase; } + +inline void SQLiteConnection::addStatement(sqlite3_stmt *statement) { myStatements.push_back(statement); } + +inline void SQLiteConnection::removeStatement(sqlite3_stmt *statement) { + std::vector::iterator it = std::find(myStatements.begin(), myStatements.end(), statement); + if (it != myStatements.end()) { + myStatements.erase(it); + } +} + +inline bool SQLiteConnection::isOpened() const { return myDatabase != 0; } + + +#endif /* __SQLITECONNECTION_H__ */ diff --git a/reader/src/database/sqldb/implsqlite/SQLiteDataBase.cpp b/reader/src/database/sqldb/implsqlite/SQLiteDataBase.cpp new file mode 100644 index 0000000..1cccc0a --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteDataBase.cpp @@ -0,0 +1,107 @@ +/* + * 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 "SQLiteDataBase.h" + +#include "SQLiteConnection.h" +#include "SQLiteCommand.h" + +//----------- Transaction subclass ----------------- + +SQLiteDataBase::Transaction::Transaction(SQLiteDataBase &db) : myDataBase(db), mySuccess(false), myStarted(false), myDepth((unsigned int)-1) { +} + +SQLiteDataBase::Transaction::~Transaction() { + if (myStarted) { + end(mySuccess); + } +} + +bool SQLiteDataBase::Transaction::start() { + myDepth = myDataBase.myTransactionDepth; + if (myDepth == 0) { + myStarted = myDataBase.myBeginTransaction->execute(); + } else { + //((DBTextValue &) *myDataBase.myMakeSavePoint->parameter("@name").value()).setValue(name()); + //myStarted = myDataBase.myMakeSavePoint->execute(); + myStarted = true; + } + if (myStarted) { + ++myDataBase.myTransactionDepth; + } + return myStarted; +} + +void SQLiteDataBase::Transaction::end(bool success) { + --myDataBase.myTransactionDepth; + if (myDepth == 0) { + if (success) { + myDataBase.myCommitTransaction->execute(); + } else { + myDataBase.myRollbackTransaction->execute(); + } + } else { + if (success) { + //((DBTextValue &) *myDataBase.myCommitSavePoint->parameter("@name").value()).setValue(name()); + //myDataBase.myCommitSavePoint->execute(); + } else { + //((DBTextValue &) *myDataBase.myRollbackSavePoint->parameter("@name").value()).setValue(name()); + //myDataBase.myRollbackSavePoint->execute(); + } + } +} + +std::string SQLiteDataBase::Transaction::name() const { + std::string name = "tran"; + ZLStringUtil::appendNumber(name, myDepth); + return name; +} + +//----------- End Transaction subclass ----------------- + + + +SQLiteDataBase::SQLiteDataBase(const std::string &path) : DataBase( new SQLiteConnection(path) ), myTransactionDepth(0) { + myBeginTransaction = SQLiteFactory::createCommand("BEGIN IMMEDIATE TRANSACTION", connection()); + myCommitTransaction = SQLiteFactory::createCommand("COMMIT TRANSACTION", connection()); + myRollbackTransaction = SQLiteFactory::createCommand("ROLLBACK TRANSACTION", connection()); + myMakeSavePoint = SQLiteFactory::createCommand("SAVEPOINT @name", connection(), "@name", DBValue::DBTEXT); + myCommitSavePoint = SQLiteFactory::createCommand("RELEASE @name", connection(), "@name", DBValue::DBTEXT); + myRollbackSavePoint = SQLiteFactory::createCommand("ROLLBACK TO @name; RELEASE @name", connection(), "@name", DBValue::DBTEXT); +} + +SQLiteDataBase::~SQLiteDataBase() { + if (connection().isOpened()) { + connection().close(); + } +} + +bool SQLiteDataBase::executeAsTransaction(DBRunnable &runnable) { + Transaction tran(*this); + if (tran.start() && runnable.run()) { + tran.setSuccessful(); + return true; + } + return false; +} + + diff --git a/reader/src/database/sqldb/implsqlite/SQLiteDataBase.h b/reader/src/database/sqldb/implsqlite/SQLiteDataBase.h new file mode 100644 index 0000000..dce8e5b --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteDataBase.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + + +#ifndef __SQLITEDATABASE_H__ +#define __SQLITEDATABASE_H__ + +#include "../DataBase.h" +#include "../DBCommand.h" + +#include "SQLiteFactory.h" + + +class SQLiteDataBase : public DataBase { + +public: + SQLiteDataBase(const std::string &path); + virtual ~SQLiteDataBase(); + + bool open(); + void close(); + +public: + bool executeAsTransaction(DBRunnable &runnable); + +private: + friend class Transaction; + + class Transaction { + public: + Transaction(SQLiteDataBase &db); + ~Transaction(); + public: + bool start(); + void setSuccessful(); + private: + void end(bool success); + std::string name() const; + private: + SQLiteDataBase &myDataBase; + bool mySuccess; + bool myStarted; + unsigned int myDepth; + }; + +private: // Transaction handling + unsigned int myTransactionDepth; + shared_ptr myBeginTransaction; + shared_ptr myCommitTransaction; + shared_ptr myRollbackTransaction; + shared_ptr myMakeSavePoint; + shared_ptr myCommitSavePoint; + shared_ptr myRollbackSavePoint; + +private: // disable copying: + SQLiteDataBase(const SQLiteDataBase &); + const SQLiteDataBase &operator = (const SQLiteDataBase &); +}; + +inline bool SQLiteDataBase::open() { return connection().open(); } +inline void SQLiteDataBase::close() { connection().close(); } + +inline void SQLiteDataBase::Transaction::setSuccessful() { mySuccess = true; } + + +#endif /* __SQLITEDATABASE_H__ */ diff --git a/reader/src/database/sqldb/implsqlite/SQLiteDataReader.cpp b/reader/src/database/sqldb/implsqlite/SQLiteDataReader.cpp new file mode 100644 index 0000000..3ea4091 --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteDataReader.cpp @@ -0,0 +1,133 @@ +/* + * 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 "SQLiteDataReader.h" + +shared_ptr SQLiteDataReader::makeDBValue(sqlite3_stmt *statement, std::size_t column) { + if (column >= (std::size_t) sqlite3_column_count(statement)) { + return 0; + } + const int type = sqlite3_column_type(statement, column); + switch (type) { + case SQLITE_INTEGER: return new DBIntValue(sqlite3_column_int(statement, column)); + case SQLITE_FLOAT: return new DBRealValue(sqlite3_column_double(statement, column)); + case SQLITE_TEXT: return new DBTextValue((const char *) sqlite3_column_text(statement, column)); + case SQLITE_NULL: return DBValue::create(DBValue::DBNULL); + } + return 0; +} + +SQLiteDataReader::SQLiteDataReader(SQLiteCommand &command) : + myCommand(command), + myCurrentStatement(0), + myLocked(true) { +} + +SQLiteDataReader::~SQLiteDataReader() { + close(); +} + +bool SQLiteDataReader::next() { + const std::vector &statements = myCommand.statements(); + while (true) { + int res = sqlite3_step(statements[myCurrentStatement]); + switch (res) { + case SQLITE_OK: + break; + case SQLITE_ROW: + return true; + case SQLITE_DONE: + if (++myCurrentStatement >= statements.size()) { + return false; + } + break; + default: + myCommand.dumpError(); + return false; + } + } +} + +bool SQLiteDataReader::reset() { + return myCommand.resetStatements(); +} + +void SQLiteDataReader::close() { + if (myLocked) { + reset(); + myCommand.unlock(); + myLocked = false; + } +} + +std::size_t SQLiteDataReader::columnsNumber() const { + sqlite3_stmt *statement = currentStatement(); + return sqlite3_column_count(statement); +} + +DBValue::ValueType SQLiteDataReader::type(std::size_t column) const { + sqlite3_stmt *statement = currentStatement(); + if (column >= (std::size_t) sqlite3_column_count(statement)) { + return DBValue::DBNULL; + } + const int type = sqlite3_column_type(statement, column); + switch (type) { + case SQLITE_INTEGER: return DBValue::DBINT; + case SQLITE_FLOAT: return DBValue::DBREAL; + case SQLITE_TEXT: return DBValue::DBTEXT; + case SQLITE_NULL: return DBValue::DBNULL; + default: + return DBValue::DBNULL; + } +} + +shared_ptr SQLiteDataReader::value(std::size_t column) const { + sqlite3_stmt *statement = currentStatement(); + return makeDBValue(statement, column); +} + +int SQLiteDataReader::intValue(std::size_t column) const { + sqlite3_stmt *statement = currentStatement(); + if (column >= (std::size_t)sqlite3_column_count(statement) || + sqlite3_column_type(statement, column) != SQLITE_INTEGER) { + return 0; + } + return sqlite3_column_int(statement, column); +} + +double SQLiteDataReader::realValue(std::size_t column) const { + sqlite3_stmt *statement = currentStatement(); + if (column >= (std::size_t)sqlite3_column_count(statement) || + sqlite3_column_type(statement, column) != SQLITE_FLOAT) { + return 0; + } + return sqlite3_column_double(statement, column); +} + +std::string SQLiteDataReader::textValue(std::size_t column, const std::string &defaultValue) const { + sqlite3_stmt *statement = currentStatement(); + if (column < (std::size_t)sqlite3_column_count(statement) && + sqlite3_column_type(statement, column) == SQLITE_TEXT) { + const char *result = (const char*)sqlite3_column_text(statement, column); + if (result != 0) { + return result; + } + } + return defaultValue; +} diff --git a/reader/src/database/sqldb/implsqlite/SQLiteDataReader.h b/reader/src/database/sqldb/implsqlite/SQLiteDataReader.h new file mode 100644 index 0000000..b220b39 --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteDataReader.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + + +#ifndef __SQLITEDATAREADER_H__ +#define __SQLITEDATAREADER_H__ + +#include "../DBDataReader.h" + +#include "SQLiteCommand.h" +#include "SQLiteConnection.h" + +class SQLiteDataReader : public DBDataReader { + +public: + static shared_ptr makeDBValue(sqlite3_stmt *statement, std::size_t column); + +public: + SQLiteDataReader(SQLiteCommand &command); + ~SQLiteDataReader(); + + bool next(); + bool reset(); + void close(); + + std::size_t columnsNumber() const; + + DBValue::ValueType type(std::size_t column) const; + + shared_ptr value(std::size_t column) const; + + int intValue(std::size_t column) const; + double realValue(std::size_t column) const; + std::string textValue(std::size_t column, const std::string &defaultValue) const; + +private: + sqlite3_stmt *currentStatement() const; + +private: + SQLiteCommand &myCommand; + std::size_t myCurrentStatement; + bool myLocked; +}; + + +inline sqlite3_stmt *SQLiteDataReader::currentStatement() const { return myCommand.statements()[myCurrentStatement]; } + +#endif /* __SQLITEDATAREADER_H__ */ diff --git a/reader/src/database/sqldb/implsqlite/SQLiteFactory.cpp b/reader/src/database/sqldb/implsqlite/SQLiteFactory.cpp new file mode 100644 index 0000000..5054cc9 --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteFactory.cpp @@ -0,0 +1,113 @@ +/* + * 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 "SQLiteFactory.h" + +#include "SQLiteCommand.h" + + +shared_ptr SQLiteFactory::createCommand(const std::string &command, DBConnection &connection) { + return new SQLiteCommand(command, connection); +} + +shared_ptr SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1) { + + shared_ptr cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + + return cmd; +} + +shared_ptr SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2) { + + shared_ptr cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + + return cmd; +} + +shared_ptr SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3) { + + shared_ptr cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + cmd->parameters().push_back( DBCommandParameter(name3, type3) ); + + return cmd; +} + +shared_ptr SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, + const std::string &name4, DBValue::ValueType type4) { + + shared_ptr cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + cmd->parameters().push_back( DBCommandParameter(name3, type3) ); + cmd->parameters().push_back( DBCommandParameter(name4, type4) ); + + return cmd; +} + +shared_ptr SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, + const std::string &name4, DBValue::ValueType type4, + const std::string &name5, DBValue::ValueType type5) { + + shared_ptr cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + cmd->parameters().push_back( DBCommandParameter(name3, type3) ); + cmd->parameters().push_back( DBCommandParameter(name4, type4) ); + cmd->parameters().push_back( DBCommandParameter(name5, type5) ); + + return cmd; +} + +shared_ptr SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, + const std::string &name4, DBValue::ValueType type4, + const std::string &name5, DBValue::ValueType type5, + const std::string &name6, DBValue::ValueType type6) { + + shared_ptr cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + cmd->parameters().push_back( DBCommandParameter(name3, type3) ); + cmd->parameters().push_back( DBCommandParameter(name4, type4) ); + cmd->parameters().push_back( DBCommandParameter(name5, type5) ); + cmd->parameters().push_back( DBCommandParameter(name6, type6) ); + + return cmd; +} + diff --git a/reader/src/database/sqldb/implsqlite/SQLiteFactory.h b/reader/src/database/sqldb/implsqlite/SQLiteFactory.h new file mode 100644 index 0000000..b58b785 --- /dev/null +++ b/reader/src/database/sqldb/implsqlite/SQLiteFactory.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + + +#ifndef __SQLITEFACTORY_H__ +#define __SQLITEFACTORY_H__ + +#include "../DBConnection.h" +#include "../DBCommand.h" + +class SQLiteFactory { + +private: + SQLiteFactory(); + +public: + static shared_ptr createCommand(const std::string &command, DBConnection &connection); + + static shared_ptr createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1); + + static shared_ptr createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2); + + static shared_ptr createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3); + + static shared_ptr createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, const std::string &name4, DBValue::ValueType type4); + + static shared_ptr createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, const std::string &name4, DBValue::ValueType type4, + const std::string &name5, DBValue::ValueType type5); + + static shared_ptr createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, const std::string &name4, DBValue::ValueType type4, + const std::string &name5, DBValue::ValueType type5, const std::string &name6, DBValue::ValueType type6); +}; + + +#endif /* __SQLITEFACTORY_H__ */ diff --git a/reader/src/encodingOption/EncodingOptionEntry.cpp b/reader/src/encodingOption/EncodingOptionEntry.cpp new file mode 100644 index 0000000..04d25d0 --- /dev/null +++ b/reader/src/encodingOption/EncodingOptionEntry.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2004-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 "EncodingOptionEntry.h" + +#include "../library/Book.h" + +AbstractEncodingEntry::AbstractEncodingEntry(const std::string ¤tValue) { + if (currentValue == Book::AutoEncoding) { + myInitialSetName = currentValue; + myInitialValues[currentValue] = currentValue; + setActive(false); + return; + } + + const std::string &value = ZLUnicodeUtil::toLower(currentValue); + + const std::vector > &sets = ZLEncodingCollection::Instance().sets(); + for (std::vector >::const_iterator it = sets.begin(); it != sets.end(); ++it) { + const std::vector &infos = (*it)->infos(); + mySetNames.push_back((*it)->name()); + std::vector &names = myValues[(*it)->name()]; + for (std::vector::const_iterator jt = infos.begin(); jt != infos.end(); ++jt) { + const std::vector &aliases = (*jt)->aliases(); + for (std::vector::const_iterator kt = aliases.begin(); kt != aliases.end(); ++kt) { + if (value == ZLUnicodeUtil::toLower(*kt)) { + myInitialSetName = (*it)->name(); + myInitialValues[myInitialSetName] = (*jt)->visibleName(); + break; + } + } + names.push_back((*jt)->visibleName()); + myValueByName[(*jt)->visibleName()] = (*jt)->name(); + } + } + + if (myInitialSetName.empty()) { + myInitialSetName = mySetNames[0]; + } +} + +const std::vector &AbstractEncodingEntry::values() const { + if (initialValue() == Book::AutoEncoding) { + static std::vector AUTO_ENCODING; + if (AUTO_ENCODING.empty()) { + AUTO_ENCODING.push_back(Book::AutoEncoding); + } + return AUTO_ENCODING; + } + std::map >::const_iterator it = myValues.find(myInitialSetName); + return it->second; +} + +const std::string &AbstractEncodingEntry::initialValue() const { + if (myInitialValues[myInitialSetName].empty()) { + std::map >::const_iterator it = myValues.find(myInitialSetName); + myInitialValues[myInitialSetName] = it->second[0]; + } + return myInitialValues[myInitialSetName]; +} + +void AbstractEncodingEntry::onAccept(const std::string &value) { + if (initialValue() != Book::AutoEncoding) { + onAcceptValue(myValueByName[value]); + } +} + +void AbstractEncodingEntry::onValueSelected(int index) { + myInitialValues[myInitialSetName] = values()[index]; +} + + + + + +EncodingEntry::EncodingEntry(ZLStringOption &encodingOption) : + AbstractEncodingEntry(encodingOption.value()), + myEncodingOption(encodingOption) { +} + +void EncodingEntry::onAcceptValue(const std::string &value) { + myEncodingOption.setValue(value); +} + + + + +EncodingSetEntry::EncodingSetEntry(AbstractEncodingEntry &encodingEntry) : myEncodingEntry(encodingEntry) { +} + +const std::string &EncodingSetEntry::initialValue() const { + return myEncodingEntry.myInitialSetName; +} + +const std::vector &EncodingSetEntry::values() const { + return myEncodingEntry.mySetNames; +} + +void EncodingSetEntry::onValueSelected(int index) { + myEncodingEntry.myInitialSetName = values()[index]; + myEncodingEntry.resetView(); +} diff --git a/reader/src/encodingOption/EncodingOptionEntry.h b/reader/src/encodingOption/EncodingOptionEntry.h new file mode 100644 index 0000000..7b43c60 --- /dev/null +++ b/reader/src/encodingOption/EncodingOptionEntry.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __ENCODINGOPTIONENTRY_H__ +#define __ENCODINGOPTIONENTRY_H__ + +#include + +#include + +class AbstractEncodingEntry : public ZLComboOptionEntry { + +public: + AbstractEncodingEntry(const std::string ¤tValue); + + const std::string &initialValue() const; + const std::vector &values() const; + void onAccept(const std::string &value); + void onValueSelected(int index); + + virtual void onAcceptValue(const std::string &value) = 0; + +private: + std::vector mySetNames; + std::map > myValues; + mutable std::map myInitialValues; + std::map myValueByName; + std::string myInitialSetName; + +friend class EncodingSetEntry; +}; + +class EncodingEntry : public AbstractEncodingEntry { + +public: + EncodingEntry(ZLStringOption &encodingOption); + + void onAcceptValue(const std::string &value); + +private: + ZLStringOption &myEncodingOption; +}; + +class EncodingSetEntry : public ZLComboOptionEntry { + +public: + EncodingSetEntry(AbstractEncodingEntry &encodingEntry); + + const std::string &initialValue() const; + const std::vector &values() const; + void onAccept(const std::string&) {} + void onValueSelected(int index); + +private: + AbstractEncodingEntry &myEncodingEntry; +}; + +#endif /* __ENCODINGOPTIONENTRY_H__ */ diff --git a/reader/src/external/ProgramCollection.cpp b/reader/src/external/ProgramCollection.cpp new file mode 100644 index 0000000..9599d90 --- /dev/null +++ b/reader/src/external/ProgramCollection.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2004-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 + +#include "ProgramCollection.h" +#include "../options/FBCategoryKey.h" + +class ProgramCollectionBuilder : public ZLXMLReader { + +public: + ProgramCollectionBuilder(ProgramCollectionMap &collectionMap); + ~ProgramCollectionBuilder(); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + +private: + ProgramCollectionMap &myCollectionMap; + shared_ptr myCurrentCollection; + shared_ptr myCurrentProgram; +}; + +static const std::string SECTION = "section"; +static const std::string PROGRAM = "program"; +static const std::string ACTION = "action"; +static const std::string OPTION = "option"; + +ProgramCollectionBuilder::ProgramCollectionBuilder(ProgramCollectionMap &collectionMap) : myCollectionMap(collectionMap) { +} + +ProgramCollectionBuilder::~ProgramCollectionBuilder() { +} + +void ProgramCollectionBuilder::startElementHandler(const char *tag, const char **attributes) { + if (SECTION == tag) { + const char *name = attributeValue(attributes, "name"); + if (name != 0) { + myCurrentCollection = myCollectionMap.myMap[name]; + if (myCurrentCollection.isNull()) { + myCurrentCollection = new ProgramCollection(name); + myCollectionMap.myMap[name] = myCurrentCollection; + } + } + } else if (!myCurrentCollection.isNull() && (PROGRAM == tag)) { + const char *name = attributeValue(attributes, "name"); + const char *protocol = attributeValue(attributes, "protocol"); + const char *testFile = attributeValue(attributes, "testFile"); + if ((name != 0) && (protocol != 0)) { + shared_ptr channel = + ZLCommunicationManager::Instance().createMessageOutputChannel(protocol, (testFile != 0) ? testFile : ""); + if (!channel.isNull()) { + std::string sName = name; + if (!sName.empty()) { + if (sName[0] == '%') { + sName = ZLResource::resource("external")[sName.substr(1)].value(); + } + myCurrentProgram = new Program(sName, channel); + myCurrentCollection->myNames.push_back(sName); + myCurrentCollection->myPrograms[sName] = myCurrentProgram; + } + } + } + } else if (!myCurrentProgram.isNull() && (ACTION == tag)) { + const char *name = attributeValue(attributes, "name"); + if (name != 0) { + static const std::string NAME = "name"; + ZLCommunicationManager::Data &data = myCurrentProgram->myCommandData[name]; + for (const char **it = attributes; (*it != 0) && (*(it + 1) != 0); it += 2) { + if (NAME != *it) { + data[*it] = *(it + 1); + } + } + } + } else if (!myCurrentProgram.isNull() && (OPTION == tag)) { + const char *name = attributeValue(attributes, "name"); + if (name != 0) { + const char *defaultValue = attributeValue(attributes, "defaultValue"); + const std::string sName = name; + const std::string sDefaultValue = (defaultValue != 0) ? defaultValue : std::string(); + myCurrentProgram->myOptions.push_back(Program::OptionDescription(sName, sDefaultValue)); + myCurrentProgram->myDefaultValues[sName] = sDefaultValue; + } + } +} + +void ProgramCollectionBuilder::endElementHandler(const char *tag) { + if (SECTION == tag) { + if (!myCurrentCollection.isNull()) { + const std::vector &names = myCurrentCollection->names(); + ZLStringOption &nameOption = myCurrentCollection->CurrentNameOption; + if (!names.empty() && (std::find(names.begin(), names.end(), nameOption.value()) == names.end())) { + nameOption.setValue(names.front()); + } + } + myCurrentCollection = 0; + myCurrentProgram = 0; + } else if (PROGRAM == tag) { + myCurrentProgram = 0; + } +} + +ProgramCollectionMap::ProgramCollectionMap() { + ProgramCollectionBuilder builder(*this); + builder.readDocument(ZLFile(ZLibrary::DefaultFilesPathPrefix() + "external.xml")); +} + +shared_ptr ProgramCollectionMap::collection(const std::string &name) const { + std::map >::const_iterator it = myMap.find(name); + return (it != myMap.end()) ? it->second : 0; +} + +ProgramCollection::ProgramCollection(const std::string &name) : + EnableCollectionOption(ZLCategoryKey::CONFIG, name, "Enabled", true), + CurrentNameOption(ZLCategoryKey::CONFIG, name, "Name", "") { +} + +const std::vector &ProgramCollection::names() const { + return myNames; +} + +shared_ptr ProgramCollection::program(const std::string &name) const { + std::map >::const_iterator it = myPrograms.find(name); + return (it != myPrograms.end()) ? it->second : 0; +} + +shared_ptr ProgramCollection::currentProgram() const { + if (!EnableCollectionOption.value()) { + return 0; + } + return program(CurrentNameOption.value()); +} + +Program::Program(const std::string &name, shared_ptr channel) : myName(name), myChannel(channel) { +} + +void Program::run(const std::string &command, const std::string ¶meter) const { + if (!myChannel.isNull()) { + std::map::const_iterator it = myCommandData.find(command); + if (it != myCommandData.end()) { + ZLCommunicationManager::Data data = it->second; + for (ZLCommunicationManager::Data::iterator jt = data.begin(); jt != data.end(); ++jt) { + if (!jt->second.empty() && jt->second[0] == '%') { + const std::string optionName = jt->second.substr(1); + std::map::const_iterator st = myDefaultValues.find(optionName); + jt->second = ZLStringOption( + FBCategoryKey::EXTERNAL, + myName, + optionName, + (st != myDefaultValues.end()) ? st->second : "").value(); + } + } + shared_ptr sender = myChannel->createSender(data); + if (!sender.isNull()) { + sender->sendStringMessage(parameter); + } + } + } +} + +const std::vector &Program::options() const { + return myOptions; +} + +Program::OptionDescription::OptionDescription(const std::string &name, const std::string &defaultValue) : OptionName(name), DefaultValue(defaultValue) { +} diff --git a/reader/src/external/ProgramCollection.h b/reader/src/external/ProgramCollection.h new file mode 100644 index 0000000..a8aa3ec --- /dev/null +++ b/reader/src/external/ProgramCollection.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __PROGRAMCOLLECTION_H__ +#define __PROGRAMCOLLECTION_H__ + +#include +#include +#include + +#include +#include +#include + +class Program { + +private: + Program(const std::string &name, shared_ptr channel); + +public: + void run(const std::string &command, const std::string ¶meter) const; + +public: + struct OptionDescription { + OptionDescription(const std::string &name, const std::string &defaultValue); + std::string OptionName; + std::string DefaultValue; + }; + const std::vector &options() const; + +private: + const std::string myName; + shared_ptr myChannel; + std::map myCommandData; + std::vector myOptions; + std::map myDefaultValues; + +friend class ProgramCollection; +friend class ProgramCollectionBuilder; +}; + +class ProgramCollection { + +public: + mutable ZLBooleanOption EnableCollectionOption; + mutable ZLStringOption CurrentNameOption; + +public: + ProgramCollection(const std::string &name); + + const std::vector &names() const; + shared_ptr currentProgram() const; + shared_ptr program(const std::string &name) const; + +private: + std::vector myNames; + std::map > myPrograms; + +friend class ProgramCollectionBuilder; +}; + +class ProgramCollectionMap { + +public: + ProgramCollectionMap(); + shared_ptr collection(const std::string &name) const; + +private: + std::map > myMap; + +friend class ProgramCollectionBuilder; +}; + +#endif /* __PROGRAMCOLLECTION_H__ */ diff --git a/reader/src/formats/EncodedTextReader.cpp b/reader/src/formats/EncodedTextReader.cpp new file mode 100644 index 0000000..12102c1 --- /dev/null +++ b/reader/src/formats/EncodedTextReader.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2004-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 "EncodedTextReader.h" + +EncodedTextReader::EncodedTextReader(const std::string &encoding) { + ZLEncodingCollection &collection = ZLEncodingCollection::Instance(); + ZLEncodingConverterInfoPtr info = collection.info(encoding); + myConverter = !info.isNull() ? info->createConverter() : collection.defaultConverter(); +} + +EncodedTextReader::~EncodedTextReader() { +} diff --git a/reader/src/formats/EncodedTextReader.h b/reader/src/formats/EncodedTextReader.h new file mode 100644 index 0000000..8035508 --- /dev/null +++ b/reader/src/formats/EncodedTextReader.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __ENCODEDTEXTREADER_H__ +#define __ENCODEDTEXTREADER_H__ + +#include + +#include + +class EncodedTextReader { + +protected: + EncodedTextReader(const std::string &encoding); + virtual ~EncodedTextReader(); + +protected: + shared_ptr myConverter; +}; + +#endif /* __ENCODEDTEXTREADER_H__ */ diff --git a/reader/src/formats/FormatPlugin.cpp b/reader/src/formats/FormatPlugin.cpp new file mode 100644 index 0000000..059a53b --- /dev/null +++ b/reader/src/formats/FormatPlugin.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2004-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 "FormatPlugin.h" + +#include "../library/Book.h" + +bool FormatPlugin::detectEncodingAndLanguage(Book &book, ZLInputStream &stream, bool force) { + std::string language = book.language(); + std::string encoding = book.encoding(); + if (!force && !encoding.empty() && !language.empty()) { + return true; + } + + bool detected = false; + + PluginCollection &collection = PluginCollection::Instance(); + if (language.empty()) { + language = collection.DefaultLanguageOption.value(); + } + if (encoding.empty()) { + encoding = collection.DefaultEncodingOption.value(); + } + if (collection.LanguageAutoDetectOption.value() && stream.open()) { + static const int BUFSIZE = 65536; + char *buffer = new char[BUFSIZE]; + const std::size_t size = stream.read(buffer, BUFSIZE); + stream.close(); + shared_ptr info = + ZLLanguageDetector().findInfo(buffer, size); + delete[] buffer; + if (!info.isNull()) { + detected = true; + if (!info->Language.empty()) { + language = info->Language; + } + encoding = info->Encoding; + if (encoding == "US-ASCII" || encoding == "ISO-8859-1") { + encoding = "windows-1252"; + } + } + } + book.setEncoding(encoding); + book.setLanguage(language); + return detected; +} + +bool FormatPlugin::detectLanguage(Book &book, ZLInputStream &stream, const std::string &encoding, bool force) { + std::string language = book.language(); + if (!force && !language.empty()) { + return true; + } + + bool detected = false; + + PluginCollection &collection = PluginCollection::Instance(); + if (language.empty()) { + language = collection.DefaultLanguageOption.value(); + } + if (collection.LanguageAutoDetectOption.value() && stream.open()) { + static const int BUFSIZE = 65536; + char *buffer = new char[BUFSIZE]; + const std::size_t size = stream.read(buffer, BUFSIZE); + stream.close(); + shared_ptr info = + ZLLanguageDetector().findInfoForEncoding(encoding, buffer, size, -20000); + delete[] buffer; + if (!info.isNull()) { + if (!info->Language.empty()) { + detected = true; + language = info->Language; + } + } + } + book.setLanguage(language); + return detected; +} + +const std::string &FormatPlugin::tryOpen(const ZLFile&) const { + static const std::string EMPTY = ""; + return EMPTY; +} + +shared_ptr FormatPlugin::coverImage(const ZLFile &file) const { + return 0; +} diff --git a/reader/src/formats/FormatPlugin.h b/reader/src/formats/FormatPlugin.h new file mode 100644 index 0000000..5e1075e --- /dev/null +++ b/reader/src/formats/FormatPlugin.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __FORMATPLUGIN_H__ +#define __FORMATPLUGIN_H__ + +#include +#include + +#include +#include + +class Book; +class BookModel; +class ZLOptionsDialog; +class ZLOptionsDialogTab; +class ZLFile; +class ZLInputStream; +class ZLImage; + +class FormatInfoPage { + +protected: + FormatInfoPage(); + +public: + virtual ~FormatInfoPage(); +}; + +class FormatPlugin { + +protected: + FormatPlugin(); + +public: + virtual ~FormatPlugin(); + + virtual bool providesMetaInfo() const = 0; + virtual bool acceptsFile(const ZLFile &file) const = 0; + virtual FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); + + virtual const std::string &tryOpen(const ZLFile &file) const; + virtual bool readMetaInfo(Book &book) const = 0; + virtual bool readLanguageAndEncoding(Book &book) const = 0; + virtual bool readModel(BookModel &model) const = 0; + virtual shared_ptr coverImage(const ZLFile &file) const; + +protected: + static bool detectEncodingAndLanguage(Book &book, ZLInputStream &stream, bool force = false); + static bool detectLanguage(Book &book, ZLInputStream &stream, const std::string &encoding, bool force = false); +}; + +class PluginCollection { + +public: + ZLBooleanOption LanguageAutoDetectOption; + ZLStringOption DefaultLanguageOption; + ZLStringOption DefaultEncodingOption; + +public: + static PluginCollection &Instance(); + static void deleteInstance(); + +private: + PluginCollection(); + +public: + shared_ptr plugin(const ZLFile &file, bool strong); + shared_ptr plugin(const Book &book); + +private: + static PluginCollection *ourInstance; + + std::vector > myPlugins; +}; + +inline FormatInfoPage::FormatInfoPage() {} +inline FormatInfoPage::~FormatInfoPage() {} +inline FormatPlugin::FormatPlugin() {} +inline FormatPlugin::~FormatPlugin() {} +inline FormatInfoPage *FormatPlugin::createInfoPage(ZLOptionsDialog&, const ZLFile&) { return 0; } + +#endif /* __FORMATPLUGIN_H__ */ diff --git a/reader/src/formats/PluginCollection.cpp b/reader/src/formats/PluginCollection.cpp new file mode 100644 index 0000000..d120de1 --- /dev/null +++ b/reader/src/formats/PluginCollection.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2004-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 "FormatPlugin.h" + +#include "../library/Book.h" + +#include "fb2/FB2Plugin.h" +//#include "docbook/DocBookPlugin.h" +#include "html/HtmlPlugin.h" +#include "txt/TxtPlugin.h" +#include "pdb/PdbPlugin.h" +#include "tcr/TcrPlugin.h" +#include "oeb/OEBPlugin.h" +#include "chm/CHMPlugin.h" +#include "rtf/RtfPlugin.h" +#include "openreader/OpenReaderPlugin.h" +#include "doc/DocPlugin.h" +//#include "pdf/PdfPlugin.h" + +PluginCollection *PluginCollection::ourInstance = 0; + +PluginCollection &PluginCollection::Instance() { + if (ourInstance == 0) { + ourInstance = new PluginCollection(); + ourInstance->myPlugins.push_back(new FB2Plugin()); + //ourInstance->myPlugins.push_back(new DocBookPlugin()); + ourInstance->myPlugins.push_back(new HtmlPlugin()); + ourInstance->myPlugins.push_back(new TxtPlugin()); + ourInstance->myPlugins.push_back(new PluckerPlugin()); + ourInstance->myPlugins.push_back(new PalmDocPlugin()); + ourInstance->myPlugins.push_back(new MobipocketPlugin()); + ourInstance->myPlugins.push_back(new EReaderPlugin()); + ourInstance->myPlugins.push_back(new ZTXTPlugin()); + ourInstance->myPlugins.push_back(new TcrPlugin()); + ourInstance->myPlugins.push_back(new CHMPlugin()); + ourInstance->myPlugins.push_back(new OEBPlugin()); + ourInstance->myPlugins.push_back(new RtfPlugin()); + ourInstance->myPlugins.push_back(new OpenReaderPlugin()); + ourInstance->myPlugins.push_back(new DocPlugin()); + //ourInstance->myPlugins.push_back(new PdfPlugin()); + } + return *ourInstance; +} + +void PluginCollection::deleteInstance() { + if (ourInstance != 0) { + delete ourInstance; + ourInstance = 0; + } +} + +PluginCollection::PluginCollection() : + LanguageAutoDetectOption(ZLCategoryKey::CONFIG, "Format", "AutoDetect", true), + DefaultLanguageOption(ZLCategoryKey::CONFIG, "Format", "DefaultLanguageS", ZLibrary::Language()), + DefaultEncodingOption(ZLCategoryKey::CONFIG, "Format", "DefaultEncoding", "UTF-8") { +} + +shared_ptr PluginCollection::plugin(const Book &book) { + return plugin(book.file(), false); +} + +shared_ptr PluginCollection::plugin(const ZLFile &file, bool strong) { + for (std::vector >::const_iterator it = myPlugins.begin(); it != myPlugins.end(); ++it) { + if ((!strong || (*it)->providesMetaInfo()) && (*it)->acceptsFile(file)) { + return *it; + } + } + return 0; +} diff --git a/reader/src/formats/chm/BitStream.cpp b/reader/src/formats/chm/BitStream.cpp new file mode 100644 index 0000000..bf6c642 --- /dev/null +++ b/reader/src/formats/chm/BitStream.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2004-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 "BitStream.h" + +const int BitStream::BufferSize = sizeof(unsigned int) * 8; + +unsigned int BitStream::get4BytesDirect() { + if (myByteStream + 4 > myByteStreamEnd) { + return 0; + } + unsigned int bytes = *myByteStream++ << 24; + bytes += *myByteStream++ << 16; + bytes += *myByteStream++ << 8; + bytes += *myByteStream++; + return bytes; +} + +bool BitStream::getBytesDirect(unsigned char *buffer, unsigned int length) { + if (myByteStream + length > myByteStreamEnd) { + return false; + } + std::memcpy(buffer, myByteStream, length); + myByteStream += length; + return true; +} diff --git a/reader/src/formats/chm/BitStream.h b/reader/src/formats/chm/BitStream.h new file mode 100644 index 0000000..80c1e25 --- /dev/null +++ b/reader/src/formats/chm/BitStream.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __BITSTREAM_H__ +#define __BITSTREAM_H__ + +#include + +class BitStream { + +public: + static const int BufferSize; + +public: + BitStream(); + void setData(const std::string &data); + void reset(); + unsigned int peek(unsigned char length); + void remove(unsigned char length); + unsigned int get(unsigned char length); + unsigned int bytesLeft() const; + + unsigned int get4BytesDirect(); + bool getBytesDirect(unsigned char *buffer, unsigned int length); + +private: + bool ensure(unsigned char length); + +private: + unsigned int myBuffer; + unsigned char myBitCounter; + const unsigned char *myByteStream; + const unsigned char *myByteStreamEnd; + +private: + BitStream(const BitStream&); + const BitStream &operator = (const BitStream&); +}; + +inline BitStream::BitStream() : myBuffer(0), myBitCounter(0) { +} + +inline void BitStream::setData(const std::string &data) { + myByteStream = (const unsigned char*)data.data(); + myByteStreamEnd = myByteStream + data.length(); + myBuffer = 0; + myBitCounter = 0; +} + +inline void BitStream::reset() { + myByteStream -= myBitCounter / 8; + myBuffer = 0; + myBitCounter = 0; +} + +inline bool BitStream::ensure(unsigned char length) { + while ((myBitCounter < length) && (bytesLeft() >= 2)) { + myBuffer |= ((myByteStream[1] << 8) | myByteStream[0]) << (BitStream::BufferSize - 16 - myBitCounter); + myBitCounter += 16; + myByteStream += 2; + } + return myBitCounter >= length; +} + +inline unsigned int BitStream::peek(unsigned char length) { + ensure(length); + return (length > 0) ? (myBuffer >> (BufferSize - length)) : 0; +} + +inline void BitStream::remove(unsigned char length) { + if (ensure(length)) { + myBuffer <<= length; + myBitCounter -= length; + } +} + +inline unsigned int BitStream::get(unsigned char length) { + unsigned int bits; + if (length > 16) { + bits = peek(length - 16) << 16; + remove(length - 16); + bits += peek(16); + remove(16); + } else { + bits = peek(length); + remove(length); + } + return bits; +} + +inline unsigned int BitStream::bytesLeft() const { + return myByteStreamEnd - myByteStream; +} + +#endif /* __BITSTREAM_H__ */ diff --git a/reader/src/formats/chm/CHMFile.cpp b/reader/src/formats/chm/CHMFile.cpp new file mode 100644 index 0000000..8c62bca --- /dev/null +++ b/reader/src/formats/chm/CHMFile.cpp @@ -0,0 +1,490 @@ +/* + * Copyright (C) 2004-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 + +#include "CHMFile.h" +#include "CHMReferenceCollection.h" + +#include "LZXDecompressor.h" + +static std::string readString(ZLInputStream &stream, std::size_t length) { + std::string string(length, ' '); + stream.read(const_cast(string.data()), length); + return string; +} + +static unsigned short readUnsignedWord(ZLInputStream &stream) { + unsigned char buffer[2]; + stream.read((char*)buffer, 2); + unsigned short result = buffer[1]; + result = result << 8; + result += buffer[0]; + return result; +} + +static unsigned long readUnsignedDWord(ZLInputStream &stream) { + unsigned long lowPart = readUnsignedWord(stream); + unsigned long highPart = readUnsignedWord(stream); + return (highPart << 16) + lowPart; +} + +static unsigned long long readUnsignedQWord(ZLInputStream &stream) { + unsigned long long lowPart = readUnsignedDWord(stream); + unsigned long long highPart = readUnsignedDWord(stream); + return (highPart << 32) + lowPart; +} + +static unsigned long long readEncodedInteger(ZLInputStream &stream) { + unsigned long long result = 0; + char part; + do { + result = result << 7; + stream.read(&part, 1); + result += part & 0x7F; + } while (part & -0x80); + return result; +} + +CHMInputStream::CHMInputStream(shared_ptr base, const CHMFileInfo::SectionInfo §ionInfo, std::size_t offset, std::size_t size) : myBase(base), mySectionInfo(sectionInfo), mySize(size) { + myBaseStartIndex = offset / 0x8000; + myBaseStartIndex -= myBaseStartIndex % sectionInfo.ResetInterval; + myBytesToSkip = offset - myBaseStartIndex * 0x8000; + myOutData = new unsigned char[0x8000]; +} + +CHMInputStream::~CHMInputStream() { + close(); + delete[] myOutData; +} + +bool CHMInputStream::open() { + myOffset = 0; + myDoSkip = true; + myBaseIndex = myBaseStartIndex; + if (myDecompressor.isNull()) { + myDecompressor = new LZXDecompressor(mySectionInfo.WindowSizeIndex); + } else { + myDecompressor->reset(); + } + myOutDataOffset = 0; + myOutDataLength = 0; + return true; +} + +std::size_t CHMInputStream::read(char *buffer, std::size_t maxSize) { + if (myDoSkip) { + do_read(0, myBytesToSkip); + myDoSkip = false; + } + std::size_t realSize = do_read(buffer, std::min(maxSize, mySize - myOffset)); + myOffset += realSize; + return realSize; +} + +std::size_t CHMInputStream::do_read(char *buffer, std::size_t maxSize) { + std::size_t realSize = 0; + do { + if (myOutDataLength == 0) { + if (myBaseIndex >= mySectionInfo.ResetTable.size()) { + break; + } + const bool isTail = myBaseIndex + 1 == mySectionInfo.ResetTable.size(); + const std::size_t start = mySectionInfo.ResetTable[myBaseIndex]; + const std::size_t end = isTail ? mySectionInfo.CompressedSize : mySectionInfo.ResetTable[myBaseIndex + 1]; + myOutDataLength = isTail ? mySectionInfo.UncompressedSize % 0x8000 : 0x8000; + myOutDataOffset = 0; + + myInData.erase(); + myInData.append(end - start, '\0'); + myBase->seek(mySectionInfo.Offset + start, true); + myBase->read((char*)myInData.data(), myInData.length()); + if (myBaseIndex % mySectionInfo.ResetInterval == 0) { + myDecompressor->reset(); + } + ++myBaseIndex; + + if (!myDecompressor->decompress(myInData, myOutData, myOutDataLength)) { + break; + } + } + const std::size_t partSize = std::min(myOutDataLength, maxSize); + if (buffer != 0) { + std::memcpy(buffer + realSize, myOutData + myOutDataOffset, partSize); + } + maxSize -= partSize; + realSize += partSize; + myOutDataLength -= partSize; + myOutDataOffset += partSize; + } while (maxSize != 0); + return realSize; +} + +void CHMInputStream::close() { + myDecompressor = 0; +} + +void CHMInputStream::seek(int offset, bool absoluteOffset) { + if (absoluteOffset) { + offset -= myOffset; + } + if (offset > 0) { + read(0, offset); + } else if (offset < 0) { + open(); + read(0, std::max(offset + (int)myOffset, 0)); + } +} + +std::size_t CHMInputStream::offset() const { + return myOffset; +} + +std::size_t CHMInputStream::sizeOfOpened() { + return mySize; +} + +shared_ptr CHMFileInfo::entryStream(shared_ptr base, const std::string &name) const { + RecordMap::const_iterator it = myRecords.find(ZLUnicodeUtil::toLower(name)); + if (it == myRecords.end()) { + return 0; + } + const RecordInfo &recordInfo = it->second; + if (recordInfo.Length == 0) { + return 0; + } + if (recordInfo.Section == 0) { + // TODO: implement + return 0; + } + if (recordInfo.Section > mySectionInfos.size()) { + return 0; + } + const SectionInfo §ionInfo = mySectionInfos[recordInfo.Section - 1]; + if (recordInfo.Offset + recordInfo.Length > sectionInfo.UncompressedSize) { + return 0; + } + + return new CHMInputStream(base, sectionInfo, recordInfo.Offset, recordInfo.Length); +} + +CHMFileInfo::CHMFileInfo(const ZLFile &file) : myFilePath(file.path()) { +} + +bool CHMFileInfo::moveToEntry(ZLInputStream &stream, const std::string &entryName) { + RecordMap::const_iterator it = myRecords.find(entryName); + if (it == myRecords.end()) { + return false; + } + RecordInfo recordInfo = it->second; + if (recordInfo.Section > mySectionInfos.size()) { + return false; + } + if (recordInfo.Section != 0) { + // TODO: ??? + return false; + } + + stream.seek(mySection0Offset + recordInfo.Offset, true); + return true; +} + +bool CHMFileInfo::init(ZLInputStream &stream) { + { + // header start + if (readString(stream, 4) != "ITSF") { + return false; + } + + unsigned long version = readUnsignedDWord(stream); + + // DWORD total length + // DWORD unknown + // DWORD timestamp + // DWORD language id + // 0x10 bytes 1st GUID + // 0x10 bytes 2nd GUID + // QWORD section 0 offset + // QWORD section 0 length + stream.seek(4 * 4 + 2 * 0x10 + 2 * 8, false); + + unsigned long long sectionOffset1 = readUnsignedQWord(stream); + unsigned long long sectionLength1 = readUnsignedQWord(stream); + mySection0Offset = sectionOffset1 + sectionLength1; + // header end + + // additional header data start + if (version > 2) { + mySection0Offset = readUnsignedQWord(stream); + } + // additional header data end + + stream.seek(sectionOffset1, true); + // header section 1 start + // directory header start + if (readString(stream, 4) != "ITSP") { + return false; + } + + // DWORD version + // DWORD length + // DWORD 0x000A + // DWORD chunk size + // DWORD density + // DWORD depth + // DWORD root chunk number + // DWORD first chunk number + // DWORD last chunk number + // DWORD -1 + stream.seek(10 * 4, false); + unsigned long dirChunkNumber = readUnsignedDWord(stream); + // ... + stream.seek(36, false); + // header section 1 end + + std::size_t nextOffset = stream.offset(); + for (unsigned long i = 0; i < dirChunkNumber; ++i) { + nextOffset += 4096; + std::string header = readString(stream, 4); + if (header == "PMGL") { + unsigned long quickRefAreaSize = readUnsignedDWord(stream) % 4096; + stream.seek(12, false); + std::size_t startOffset = stream.offset(); + std::size_t oldOffset = startOffset; + while (startOffset < nextOffset - quickRefAreaSize) { + int nameLength = readEncodedInteger(stream); + std::string name = readString(stream, nameLength); + int contentSection = readEncodedInteger(stream); + int offset = readEncodedInteger(stream); + int length = readEncodedInteger(stream); + if (name.substr(0, 2) != "::") { + name = ZLUnicodeUtil::toLower(name); + } + myRecords.insert( + std::make_pair( + name, + CHMFileInfo::RecordInfo(contentSection, offset, length) + ) + ); + startOffset = stream.offset(); + if (oldOffset == startOffset) { + break; + } + oldOffset = startOffset; + } + } else if (header == "PMGI") { + unsigned long quickRefAreaSize = readUnsignedDWord(stream); + std::size_t startOffset = stream.offset(); + std::size_t oldOffset = startOffset; + while (startOffset < nextOffset - quickRefAreaSize) { + int nameLength = readEncodedInteger(stream); + std::string name = readString(stream, nameLength); + // chunk number + readEncodedInteger(stream); + startOffset = stream.offset(); + if (oldOffset == startOffset) { + break; + } + oldOffset = startOffset; + } + } + stream.seek(nextOffset, true); + if (stream.offset() != nextOffset) { + break; + } + } + } + + { + if (!moveToEntry(stream, "::DataSpace/NameList")) { + return false; + } + stream.seek(2, false); + const int sectionNumber = readUnsignedWord(stream); + for (int i = 0; i < sectionNumber; ++i) { + const int length = readUnsignedWord(stream); + std::string sectionName; + sectionName.reserve(length); + for (int j = 0; j < length; ++j) { + sectionName += (char)readUnsignedWord(stream); + } + stream.seek(2, false); + mySectionNames.push_back(sectionName); + } + } + + { + for (unsigned int i = 1; i < mySectionNames.size(); ++i) { + RecordMap::const_iterator it = + myRecords.find("::DataSpace/Storage/" + mySectionNames[i] + "/Content"); + if (it == myRecords.end()) { + return false; + } + RecordInfo recordInfo = it->second; + if (recordInfo.Section != 0) { + return false; + } + mySectionInfos.push_back(SectionInfo()); + SectionInfo &info = mySectionInfos.back(); + info.Offset = mySection0Offset + recordInfo.Offset; + info.Length = recordInfo.Length; + + if (!moveToEntry(stream, "::DataSpace/Storage/" + mySectionNames[i] + "/ControlData")) { + return false; + } + stream.seek(4, false); + std::string lzxc = readString(stream, 4); + if (lzxc != "LZXC") { + return false; + } + const int version = readUnsignedDWord(stream); + if ((version <= 0) || (version > 2)) { + return false; + } + info.ResetInterval = readUnsignedDWord(stream); + if (version == 1) { + info.ResetInterval /= 0x8000; + } + info.WindowSizeIndex = (version == 1) ? 0 : 15; + { + int ws = readUnsignedDWord(stream); + if (ws > 0) { + while ((ws & 1) == 0) { + ws >>= 1; + info.WindowSizeIndex++; + } + } + } + + if (!moveToEntry(stream, "::DataSpace/Storage/" + mySectionNames[i] + "/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable")) { + return false; + } + stream.seek(4, false); + const std::size_t entriesNumber = readUnsignedDWord(stream); + if (entriesNumber == 0) { + return false; + } + if (entriesNumber > 2048) { + // file size is greater than 60 Mb + return false; + } + info.ResetTable.reserve(entriesNumber); + stream.seek(8, false); + info.UncompressedSize = readUnsignedQWord(stream); + if ((info.UncompressedSize - 1) / 0x8000 != entriesNumber - 1) { + return false; + } + info.CompressedSize = readUnsignedQWord(stream); + stream.seek(8, false); + std::size_t previous = 0; + for (std::size_t j = 0; j < entriesNumber; ++j) { + std::size_t value = readUnsignedQWord(stream); + if ((j > 0) == (value <= previous)) { + return false; + } + info.ResetTable.push_back(value); + previous = value; + } + } + } + + return true; +} + +static std::string readNTString(ZLInputStream &stream) { + std::string s; + char c; + while (stream.read(&c, 1) == 1) { + if (c == '\0') { + break; + } else { + s += c; + } + } + return CHMReferenceCollection::fullReference("/", s); +} + +bool CHMFileInfo::FileNames::empty() const { + return Start.empty() && TOC.empty() && Home.empty() && Index.empty(); +} + +CHMFileInfo::FileNames CHMFileInfo::sectionNames(shared_ptr base) const { + FileNames names; + shared_ptr stringsStream = entryStream(base, "/#STRINGS"); + if (!stringsStream.isNull() && stringsStream->open()) { + std::vector fileNames; + int tocIndex = -1; + int indexIndex = -1; + for (int i = 0; i < 12; ++i) { + std::string argument = readNTString(*stringsStream); + if (argument.empty() || (argument[argument.length() - 1] == '/')) { + continue; + } + if (myRecords.find(argument) == myRecords.end()) { + continue; + } + if ((tocIndex == -1) && ZLStringUtil::stringEndsWith(argument, ".hhc")) { + tocIndex = fileNames.size(); + names.TOC = argument; + } else if ((indexIndex == -1) && ZLStringUtil::stringEndsWith(argument, ".hhk")) { + indexIndex = fileNames.size(); + names.Index = argument; + } + fileNames.push_back(argument); + } + std::size_t startIndex = std::max(3, std::max(tocIndex, indexIndex) + 1); + if (startIndex < 11) { + if (startIndex < fileNames.size()) { + names.Start = fileNames[startIndex]; + } + if (startIndex + 1 < fileNames.size()) { + names.Home = fileNames[startIndex + 1]; + } + } + stringsStream->close(); + } + if (names.TOC.empty()) { + for (RecordMap::const_iterator it = myRecords.begin(); it != myRecords.end(); ++it) { + if (ZLStringUtil::stringEndsWith(it->first, ".hhc")) { + names.TOC = it->first; + break; + } + } + } + if (names.empty()) { + for (RecordMap::const_iterator it = myRecords.begin(); it != myRecords.end(); ++it) { + if ((ZLStringUtil::stringEndsWith(it->first, ".htm")) || + (ZLStringUtil::stringEndsWith(it->first, ".html"))) { + names.Start = it->first; + break; + } + } + } + + return names; +} + +const std::string CHMFileInfo::filePath() const { + return myFilePath; +} diff --git a/reader/src/formats/chm/CHMFile.h b/reader/src/formats/chm/CHMFile.h new file mode 100644 index 0000000..d98bd84 --- /dev/null +++ b/reader/src/formats/chm/CHMFile.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __CHMFILE_H__ +#define __CHMFILE_H__ + +#include +#include +#include + +#include +#include + +class ZLFile; + +class LZXDecompressor; + +class CHMFileInfo { + +public: + struct FileNames { + std::string TOC; + std::string Index; + std::string Start; + std::string Home; + + bool empty() const; + }; + +public: + CHMFileInfo(const ZLFile &file); + bool init(ZLInputStream &stream); + // We assume that base exists and is already open + shared_ptr entryStream(shared_ptr base, const std::string &name) const; + // We assume that base exists and is already open + FileNames sectionNames(shared_ptr base) const; + const std::string filePath() const; + +private: + bool moveToEntry(ZLInputStream &stream, const std::string &entryName); + +private: + unsigned long long mySection0Offset; + + struct RecordInfo { + RecordInfo(int section, int offset, int length) : Section(section), Offset(offset), Length(length) {} + std::size_t Section; + std::size_t Offset; + std::size_t Length; + }; + + typedef std::map RecordMap; + RecordMap myRecords; + std::vector mySectionNames; + + struct SectionInfo { + std::size_t WindowSizeIndex; + std::size_t ResetInterval; + std::size_t Offset; + std::size_t Length; + std::size_t CompressedSize; + std::size_t UncompressedSize; + std::vector ResetTable; + }; + std::vector mySectionInfos; + + const std::string myFilePath; + +private: + CHMFileInfo(const CHMFileInfo&); + const CHMFileInfo &operator = (const CHMFileInfo&); + +friend class CHMInputStream; +}; + +class CHMInputStream : public ZLInputStream { + +public: + CHMInputStream(shared_ptr base, const CHMFileInfo::SectionInfo §ionInfo, std::size_t offset, std::size_t size); + ~CHMInputStream(); + + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +private: + std::size_t do_read(char *buffer, std::size_t maxSize); + +private: + shared_ptr myBase; + const CHMFileInfo::SectionInfo mySectionInfo; + std::size_t myBaseStartIndex; + std::size_t myBaseIndex; + std::size_t myBytesToSkip; + const std::size_t mySize; + + std::size_t myOffset; + bool myDoSkip; + + shared_ptr myDecompressor; + std::string myInData; + + unsigned char *myOutData; + std::size_t myOutDataOffset; + std::size_t myOutDataLength; +}; + +#endif /* __CHMFILE_H__ */ diff --git a/reader/src/formats/chm/CHMFileImage.cpp b/reader/src/formats/chm/CHMFileImage.cpp new file mode 100644 index 0000000..a2b58f0 --- /dev/null +++ b/reader/src/formats/chm/CHMFileImage.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2004-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 "CHMFileImage.h" + +CHMFileImage::CHMFileImage(shared_ptr info, const std::string &entry) : ZLStreamImage(ZLMimeType::IMAGE_AUTO, 0, 0), myInfo(info), myEntry(entry) { +} + +shared_ptr CHMFileImage::inputStream() const { + shared_ptr baseStream = ZLFile(myInfo->filePath()).inputStream(); + if (baseStream.isNull() || !baseStream->open()) { + return 0; + } + return myInfo->entryStream(baseStream, myEntry); +} diff --git a/reader/src/formats/chm/CHMFileImage.h b/reader/src/formats/chm/CHMFileImage.h new file mode 100644 index 0000000..bacb6aa --- /dev/null +++ b/reader/src/formats/chm/CHMFileImage.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __CHMFILEIMAGE_H__ +#define __CHMFILEIMAGE_H__ + +#include + +#include "CHMFile.h" + +class CHMFileImage : public ZLStreamImage { + +public: + CHMFileImage(shared_ptr info, const std::string &entry); + +private: + shared_ptr inputStream() const; + +private: + shared_ptr myInfo; + std::string myEntry; +}; + +#endif /* __CHMFILEIMAGE_H__ */ diff --git a/reader/src/formats/chm/CHMPlugin.cpp b/reader/src/formats/chm/CHMPlugin.cpp new file mode 100644 index 0000000..9ea88e4 --- /dev/null +++ b/reader/src/formats/chm/CHMPlugin.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2004-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 "CHMPlugin.h" +#include "CHMFile.h" +#include "CHMFileImage.h" +#include "CHMReferenceCollection.h" +#include "HHCReader.h" +#include "HHCReferenceCollector.h" +#include "../txt/PlainTextFormat.h" +#include "HtmlSectionReader.h" +#include "../util/MergedStream.h" +#include "../html/HtmlReaderStream.h" + +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +bool CHMPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "chm"; +} + +class CHMTextStream : public MergedStream { + +public: + CHMTextStream(CHMFileInfo &chmFile, shared_ptr base); + +private: + void resetToStart(); + shared_ptr nextStream(); + +private: + CHMFileInfo &myCHMFile; + shared_ptr myBase; + std::vector myEntryNames; + std::size_t myIndex; +}; + +CHMTextStream::CHMTextStream(CHMFileInfo &chmFile, shared_ptr base) : myCHMFile(chmFile), myBase(base) { +} + +void CHMTextStream::resetToStart() { + myIndex = 0; + + if (!myEntryNames.empty()) { + return; + } + + CHMFileInfo::FileNames names = myCHMFile.sectionNames(myBase); + if (names.empty()) { + return; + } + + CHMReferenceCollection referenceCollection; + + referenceCollection.addReference(names.Start, false); + referenceCollection.addReference(names.Home, false); + + shared_ptr tocStream = myCHMFile.entryStream(myBase, names.TOC); + if (!tocStream.isNull() && tocStream->open()) { + referenceCollection.setPrefix(names.TOC); + HHCReferenceCollector(referenceCollection).readDocument(*tocStream); + } + + while (referenceCollection.containsNonProcessedReferences()) { + myEntryNames.push_back(referenceCollection.nextReference()); + } +} + +shared_ptr CHMTextStream::nextStream() { + while (myIndex < myEntryNames.size()) { + shared_ptr stream = myCHMFile.entryStream(myBase, myEntryNames[myIndex++]); + if (!stream.isNull()) { + return new HtmlReaderStream(stream, 50000); + } + } + return 0; +} + +bool CHMPlugin::readMetaInfo(Book &book) const { + const ZLFile &file = book.file(); + shared_ptr stream = file.inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + + CHMFileInfo chmFile(file); + if (!chmFile.init(*stream)) { + return false; + } + + CHMFileInfo::FileNames names = chmFile.sectionNames(stream); + if (names.empty()) { + return false; + } + + /* + shared_ptr entryStream = chmFile.entryStream(stream, names.Start); + if (entryStream.isNull()) { + entryStream = chmFile.entryStream(stream, names.Home); + } + if (entryStream.isNull()) { + entryStream = chmFile.entryStream(stream, names.TOC); + } + / * + if (entryStream.isNull()) { + chmFile.entryStream(stream, names.Index); + } + * / + if (entryStream.isNull()) { + return false; + } + */ + + CHMTextStream textStream(chmFile, stream); + detectEncodingAndLanguage(book, textStream); + if (book.encoding().empty()) { + return false; + } + + return true; +} + +bool CHMPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} + +class CHMHyperlinkMatcher : public BookModel::HyperlinkMatcher { + +public: + BookModel::Label match(const std::map &lMap, const std::string &id) const; +}; + +BookModel::Label CHMHyperlinkMatcher::match(const std::map &lMap, const std::string &id) const { + std::map::const_iterator it = lMap.find(id); + if (it != lMap.end()) { + return it->second; + } + std::size_t index = id.find('#'); + if (index != std::string::npos) { + it = lMap.find(id.substr(0, index)); + } + return (it != lMap.end()) ? it->second : BookModel::Label(0, -1); +} + +bool CHMPlugin::readModel(BookModel &model) const { + model.setHyperlinkMatcher(new CHMHyperlinkMatcher()); + + const Book &book = *model.book(); + const ZLFile &file = book.file(); + + shared_ptr stream = file.inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + + shared_ptr info = new CHMFileInfo(file); + if (!info->init(*stream)) { + return false; + } + + CHMFileInfo::FileNames names = info->sectionNames(stream); + if (names.empty()) { + return false; + } + + CHMReferenceCollection referenceCollection; + + referenceCollection.addReference(names.Start, false); + referenceCollection.addReference(names.Home, false); + + const std::string &encoding = book.encoding(); + + shared_ptr tocStream = info->entryStream(stream, names.TOC); + HHCReader hhcReader(referenceCollection, model, encoding); + if (!tocStream.isNull() && tocStream->open()) { + referenceCollection.setPrefix(names.TOC); + hhcReader.readDocument(*tocStream); + } + + /* + if (!tocStream.isNull() && tocStream->open()) { + std::string buf; + buf.append(tocStream->sizeOfOpened(), '\0'); + tocStream->read((char*)buf.data(), buf.length()); + std::cerr << "[ " << names.TOC << " ]\n" << buf << "\n"; + } + */ + + int contentCounter = 0; + PlainTextFormat format(file); + HtmlSectionReader reader(model, format, encoding, info, referenceCollection); + while (referenceCollection.containsNonProcessedReferences()) { + const std::string fileName = referenceCollection.nextReference(); + if (ZLStringUtil::stringEndsWith(fileName, ".jpg") || + ZLStringUtil::stringEndsWith(fileName, ".gif")) { + std::string lowerCasedFileName = ZLUnicodeUtil::toLower(fileName); + BookReader bookReader(model); + bookReader.setMainTextModel(); + bookReader.addHyperlinkLabel(lowerCasedFileName); + bookReader.pushKind(REGULAR); + bookReader.beginParagraph(); + bookReader.addImageReference(lowerCasedFileName); + bookReader.addImage(fileName, new CHMFileImage(info, fileName)); + bookReader.endParagraph(); + bookReader.insertEndOfTextParagraph(); + } else { + shared_ptr entryStream = info->entryStream(stream, fileName); + if (!entryStream.isNull() && entryStream->open()) { + /* + std::string buf; + buf.append(entryStream->sizeOfOpened(), '\0'); + entryStream->read((char*)buf.data(), buf.length()); + std::cerr << "[ " << fileName << " ]\n" << buf << "\n"; + entryStream->open(); + */ + reader.setSectionName(fileName); + reader.readDocument(*entryStream); + ++contentCounter; + } + } + } + if (contentCounter == 0) { + return false; + } + + hhcReader.setReferences(); + + + return true; +} diff --git a/reader/src/formats/chm/CHMPlugin.h b/reader/src/formats/chm/CHMPlugin.h new file mode 100644 index 0000000..0d38e62 --- /dev/null +++ b/reader/src/formats/chm/CHMPlugin.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __CHMPLUGIN_H__ +#define __CHMPLUGIN_H__ + +#include "../FormatPlugin.h" + +class CHMPlugin : public FormatPlugin { + +public: + CHMPlugin(); + ~CHMPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; +}; + +inline CHMPlugin::CHMPlugin() {} +inline CHMPlugin::~CHMPlugin() {} +inline bool CHMPlugin::providesMetaInfo() const { return false; } + +#endif /* __CHMPLUGIN_H__ */ diff --git a/reader/src/formats/chm/CHMReferenceCollection.cpp b/reader/src/formats/chm/CHMReferenceCollection.cpp new file mode 100644 index 0000000..f29dd28 --- /dev/null +++ b/reader/src/formats/chm/CHMReferenceCollection.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2004-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 "CHMReferenceCollection.h" +#include "../util/MiscUtil.h" + +std::string CHMReferenceCollection::fullReference(const std::string &prefix, std::string reference) { + reference = MiscUtil::decodeHtmlURL(reference); + if ((reference.length() > 0) && (reference[0] == '/')) { + return reference; + } + const int index = reference.rfind("::"); + if (index != -1) { + return reference.substr(index + 2); + } + + int counter = 0; + while (reference.substr(counter * 3, 3) == "../") { + ++counter; + } + + int slashIndex = prefix.length() - 1; + for (int i = 0; (i < counter) && (slashIndex > 0); ++i) { + slashIndex = prefix.rfind('/', slashIndex - 1); + } + return prefix.substr(0, slashIndex + 1) + reference.substr(counter * 3); +} + +CHMReferenceCollection::CHMReferenceCollection() : myPrefix("/") { +} + +const std::string &CHMReferenceCollection::addReference(const std::string &reference, bool doConvert) { + if (reference.empty()) { + return reference; + } + std::string fullRef = doConvert ? fullReference(myPrefix, reference) : MiscUtil::decodeHtmlURL(reference); + + const int index = fullRef.find('#'); + if (index == -1) { + fullRef = ZLUnicodeUtil::toLower(fullRef); + } else { + fullRef = ZLUnicodeUtil::toLower(fullRef.substr(0, index)); + } + std::set::const_iterator it = myReferences.find(fullRef); + if (it != myReferences.end()) { + return *it; + } + + myReferences.insert(fullRef); + myReferenceQueue.push(fullRef); + return myReferenceQueue.back(); +} + +bool CHMReferenceCollection::containsNonProcessedReferences() const { + return !myReferenceQueue.empty(); +} + +const std::string CHMReferenceCollection::nextReference() { + if (myReferenceQueue.empty()) { + return ""; + } + const std::string front = myReferenceQueue.front(); + myReferenceQueue.pop(); + return front; +} + +void CHMReferenceCollection::setPrefix(const std::string &fileName) { + myPrefix = MiscUtil::decodeHtmlURL(fileName.substr(0, fileName.rfind('/') + 1)); +} + +const std::string &CHMReferenceCollection::prefix() const { + return myPrefix; +} diff --git a/reader/src/formats/chm/CHMReferenceCollection.h b/reader/src/formats/chm/CHMReferenceCollection.h new file mode 100644 index 0000000..6a53c45 --- /dev/null +++ b/reader/src/formats/chm/CHMReferenceCollection.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __CHMREFERENCECOLLECTION_H__ +#define __CHMREFERENCECOLLECTION_H__ + +#include +#include +#include + +class CHMReferenceCollection { + +public: + static std::string fullReference(const std::string &prefix, std::string reference); + +public: + CHMReferenceCollection(); + const std::string &addReference(const std::string &reference, bool doConvert); + bool containsNonProcessedReferences() const; + const std::string nextReference(); + void setPrefix(const std::string &fileName); + const std::string &prefix() const; + +private: + std::string myPrefix; + std::set myReferences; + std::queue myReferenceQueue; + +private: + CHMReferenceCollection(const CHMReferenceCollection&); + const CHMReferenceCollection &operator = (const CHMReferenceCollection&); +}; + +#endif /* __CHMREFERENCECOLLECTION_H__ */ diff --git a/reader/src/formats/chm/E8Decoder.cpp b/reader/src/formats/chm/E8Decoder.cpp new file mode 100644 index 0000000..53b9335 --- /dev/null +++ b/reader/src/formats/chm/E8Decoder.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2004-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 "LZXDecompressor.h" + +void LZXDecompressor::E8Decoder::reset(unsigned int fileSize) { + myFileSize = fileSize; + myFramesCounter = 0; + myPosition = 0; +} + +void LZXDecompressor::E8Decoder::decode(unsigned char *buffer, const std::size_t size) { + if (myFramesCounter >= 32768) { + return; + } + ++myFramesCounter; + if (myFileSize == 0) { + return; + } + + myPosition += size; + + if (size <= 10) { + return; + } + + const unsigned char *end = buffer + size - 10; + + for (unsigned char *ptr = buffer; ptr < end; ) { + if (*ptr == 0xE8) { + int absoluteOffset = + ptr[1] + (ptr[2] << 8) + (ptr[3] << 16) + (ptr[4] << 24); + int relativeOffset = + (absoluteOffset >= 0) ? + absoluteOffset - (ptr - buffer) : absoluteOffset + myFileSize; + ptr[1] = (unsigned char)relativeOffset; + ptr[2] = (unsigned char)(relativeOffset >> 8); + ptr[3] = (unsigned char)(relativeOffset >> 16); + ptr[4] = (unsigned char)(relativeOffset >> 24); + ptr += 5; + } else { + ++ptr; + } + } +} diff --git a/reader/src/formats/chm/HHCReader.cpp b/reader/src/formats/chm/HHCReader.cpp new file mode 100644 index 0000000..4fd3105 --- /dev/null +++ b/reader/src/formats/chm/HHCReader.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2004-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 "HHCReader.h" +#include "CHMReferenceCollection.h" + +HHCReader::HHCReader(CHMReferenceCollection &collection, BookModel &model, const std::string &encoding) : HtmlReader(encoding), myReferenceCollection(collection), myBookReader(model) { +} + +HHCReader::~HHCReader() { +} + +void HHCReader::startDocumentHandler() { + myBookReader.setMainTextModel(); +} + +void HHCReader::endDocumentHandler() { + std::string tmp0; + myText.swap(tmp0); + std::string tmp1; + myReference.swap(tmp1); +} + +static const std::string UL = "UL"; +static const std::string LI = "LI"; +static const std::string OBJECT = "OBJECT"; +static const std::string PARAM = "PARAM"; +static const std::string NAME = "NAME"; +static const std::string VALUE = "VALUE"; +static const std::string NAME_VALUE = "Name"; +static const std::string LOCAL_VALUE = "Local"; + +static bool isFirstChild = false; + +bool HHCReader::tagHandler(const HtmlTag &tag) { + if (tag.Start) { + if (tag.Name == UL) { + isFirstChild = true; + } else if (tag.Name == LI) { + } else if (tag.Name == OBJECT) { + myText.erase(); + myReference.erase(); + } else if (tag.Name == PARAM) { + std::string name; + std::string value; + for (std::vector::const_iterator it = tag.Attributes.begin(); it != tag.Attributes.end(); ++it) { + if (it->Name == NAME) { + name = it->Value; + } else if (it->Name == VALUE) { + value = it->Value; + } + } + if (name == NAME_VALUE) { + myText = value; + } else if (name == LOCAL_VALUE) { + myReference = myReferenceCollection.addReference(value, true); + } + } + } else { + if (tag.Name == UL) { + myBookReader.endContentsParagraph(); + } else if (tag.Name == OBJECT) { + if (!myText.empty() || !myReference.empty()) { + if (!isFirstChild) { + myBookReader.endContentsParagraph(); + } else { + isFirstChild = false; + } + myBookReader.beginContentsParagraph(); + if (myText.empty()) { + myText = "..."; + } + myBookReader.addContentsData(myText.empty() ? "..." : myText); + myReferenceVector.push_back(ZLUnicodeUtil::toLower(myReference)); + } + } + } + return true; +} + +bool HHCReader::characterDataHandler(const char*, std::size_t, bool) { + return true; +} + +void HHCReader::setReferences() { + for (std::size_t i = 0; i < myReferenceVector.size(); ++i) { + myBookReader.setReference(i, myBookReader.model().label(myReferenceVector[i]).ParagraphNumber); + } +} diff --git a/reader/src/formats/chm/HHCReader.h b/reader/src/formats/chm/HHCReader.h new file mode 100644 index 0000000..c0e4cef --- /dev/null +++ b/reader/src/formats/chm/HHCReader.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __HHCREADER_H__ +#define __HHCREADER_H__ + +#include + +#include "../html/HtmlReader.h" +#include "../../bookmodel/BookModel.h" +#include "../../bookmodel/BookReader.h" + +class CHMReferenceCollection; + +class HHCReader : public HtmlReader { + +public: + HHCReader(CHMReferenceCollection &collection, BookModel &model, const std::string &encoding); + ~HHCReader(); + + void setReferences(); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char*, std::size_t, bool); + +private: + CHMReferenceCollection &myReferenceCollection; + + std::string myText; + std::string myReference; + + BookReader myBookReader; + + std::vector myReferenceVector; +}; + +#endif /* __HHCREADER_H__ */ diff --git a/reader/src/formats/chm/HHCReferenceCollector.cpp b/reader/src/formats/chm/HHCReferenceCollector.cpp new file mode 100644 index 0000000..6abcef2 --- /dev/null +++ b/reader/src/formats/chm/HHCReferenceCollector.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008-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 "HHCReferenceCollector.h" +#include "CHMReferenceCollection.h" + +HHCReferenceCollector::HHCReferenceCollector(CHMReferenceCollection &collection) : HtmlReader("US-ASCII"), myReferenceCollection(collection) { +} + +void HHCReferenceCollector::startDocumentHandler() { +} + +void HHCReferenceCollector::endDocumentHandler() { +} + +static const std::string PARAM = "PARAM"; +static const std::string NAME = "NAME"; +static const std::string VALUE = "VALUE"; +static const std::string NAME_VALUE = "Name"; +static const std::string LOCAL_VALUE = "Local"; + +bool HHCReferenceCollector::tagHandler(const HtmlTag &tag) { + if (tag.Start) { + if (tag.Name == PARAM) { + std::string name; + std::string value; + for (std::vector::const_iterator it = tag.Attributes.begin(); it != tag.Attributes.end(); ++it) { + if (it->Name == NAME) { + name = it->Value; + } else if (it->Name == VALUE) { + value = it->Value; + } + } + if (name == LOCAL_VALUE) { + myReferenceCollection.addReference(value, true); + } + } + } + return true; +} + +bool HHCReferenceCollector::characterDataHandler(const char*, std::size_t, bool) { + return true; +} diff --git a/reader/src/formats/chm/HHCReferenceCollector.h b/reader/src/formats/chm/HHCReferenceCollector.h new file mode 100644 index 0000000..20e58d1 --- /dev/null +++ b/reader/src/formats/chm/HHCReferenceCollector.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __HHCREFERENCECOLLECTOR_H__ +#define __HHCREFERENCECOLLECTOR_H__ + +#include + +#include "../html/HtmlReader.h" + +class CHMReferenceCollection; + +class HHCReferenceCollector : public HtmlReader { + +public: + HHCReferenceCollector(CHMReferenceCollection &collection); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char*, std::size_t, bool); + +private: + CHMReferenceCollection &myReferenceCollection; +}; + +#endif /* __HHCREFERENCECOLLECTOR_H__ */ diff --git a/reader/src/formats/chm/HtmlSectionReader.cpp b/reader/src/formats/chm/HtmlSectionReader.cpp new file mode 100644 index 0000000..9973e14 --- /dev/null +++ b/reader/src/formats/chm/HtmlSectionReader.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2004-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 "HtmlSectionReader.h" +#include "CHMReferenceCollection.h" +#include "CHMFileImage.h" +#include "../util/MiscUtil.h" +#include "../html/HtmlTagActions.h" + +class HtmlSectionHrefTagAction : public HtmlHrefTagAction { + +public: + HtmlSectionHrefTagAction(HtmlSectionReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class HtmlSectionImageTagAction : public HtmlTagAction { + +public: + HtmlSectionImageTagAction(HtmlSectionReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +shared_ptr HtmlSectionReader::createAction(const std::string &tag) { + if (tag == "IMG") { + return new HtmlSectionImageTagAction(*this); + } else if (tag == "A") { + return new HtmlSectionHrefTagAction(*this); + } + return HtmlBookReader::createAction(tag); +} + +HtmlSectionReader::HtmlSectionReader(BookModel &model, const PlainTextFormat &format, const std::string &encoding, shared_ptr info, CHMReferenceCollection &collection) : HtmlBookReader("", model, format, encoding), myInfo(info), myReferenceCollection(collection) { + setBuildTableOfContent(false); +} + +void HtmlSectionReader::setSectionName(const std::string §ionName) { + myCurrentSectionName = ZLUnicodeUtil::toLower(sectionName); + myReferenceCollection.setPrefix(myCurrentSectionName); +} + +void HtmlSectionReader::startDocumentHandler() { + HtmlBookReader::startDocumentHandler(); + myBookReader.addHyperlinkLabel(ZLUnicodeUtil::toLower(myCurrentSectionName)); +} + +void HtmlSectionReader::endDocumentHandler() { + HtmlBookReader::endDocumentHandler(); + myBookReader.insertEndOfTextParagraph(); +} + +HtmlSectionHrefTagAction::HtmlSectionHrefTagAction(HtmlSectionReader &reader) : HtmlHrefTagAction(reader) { +} + +void HtmlSectionHrefTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + HtmlSectionReader &reader = (HtmlSectionReader&)myReader; + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "NAME") { + bookReader().addHyperlinkLabel(ZLUnicodeUtil::toLower(reader.myCurrentSectionName + '#' + tag.Attributes[i].Value)); + } else if ((hyperlinkType() == REGULAR) && (tag.Attributes[i].Name == "HREF")) { + const std::string &value = tag.Attributes[i].Value; + if (!value.empty()) { + FBTextKind referenceType = MiscUtil::referenceType(value); + if (referenceType != INTERNAL_HYPERLINK) { + bookReader().addHyperlinkControl(referenceType, value); + setHyperlinkType(referenceType); + } else { + const int index = value.find('#'); + std::string sectionName = (index == -1) ? value : value.substr(0, index); + sectionName = ZLUnicodeUtil::toLower(MiscUtil::decodeHtmlURL(sectionName)); + if (sectionName.empty()) { + sectionName = reader.myCurrentSectionName; + } else { + sectionName = reader.myReferenceCollection.addReference(sectionName, true); + } + bookReader().addHyperlinkControl( + INTERNAL_HYPERLINK, ZLUnicodeUtil::toLower((index == -1) ? sectionName : (sectionName + value.substr(index))) + ); + setHyperlinkType(INTERNAL_HYPERLINK); + } + } + } + } + } else if (hyperlinkType() != REGULAR) { + bookReader().addControl(hyperlinkType(), false); + setHyperlinkType(REGULAR); + } +} + +HtmlSectionImageTagAction::HtmlSectionImageTagAction(HtmlSectionReader &reader) : HtmlTagAction(reader) { +} + +void HtmlSectionImageTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + //bookReader().endParagraph(); + HtmlSectionReader &reader = (HtmlSectionReader&)myReader; + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "SRC") { + std::string fileName = MiscUtil::decodeHtmlURL(tag.Attributes[i].Value); + fileName = CHMReferenceCollection::fullReference(reader.myReferenceCollection.prefix(), fileName); + fileName = ZLUnicodeUtil::toLower(fileName); + bookReader().addImageReference(fileName); + bookReader().addImage(fileName, new CHMFileImage(reader.myInfo, fileName)); + break; + } + } + //bookReader().beginParagraph(); + } +} diff --git a/reader/src/formats/chm/HtmlSectionReader.h b/reader/src/formats/chm/HtmlSectionReader.h new file mode 100644 index 0000000..424c178 --- /dev/null +++ b/reader/src/formats/chm/HtmlSectionReader.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __HTMLSECTIONREADER_H__ +#define __HTMLSECTIONREADER_H__ + +#include "../html/HtmlBookReader.h" +#include "CHMFile.h" + +class CHMReferenceCollection; + +class HtmlSectionReader : public HtmlBookReader { + +public: + HtmlSectionReader(BookModel &model, const PlainTextFormat &format, const std::string &encoding, shared_ptr info, CHMReferenceCollection &collection); + void setSectionName(const std::string §ionName); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + +private: + shared_ptr createAction(const std::string &tag); + +private: + shared_ptr myInfo; + CHMReferenceCollection &myReferenceCollection; + std::string myCurrentSectionName; + +friend class HtmlSectionHrefTagAction; +friend class HtmlSectionImageTagAction; +}; + +#endif /* __HTMLSECTIONREADER_H__ */ diff --git a/reader/src/formats/chm/HuffmanDecoder.cpp b/reader/src/formats/chm/HuffmanDecoder.cpp new file mode 100644 index 0000000..db8718f --- /dev/null +++ b/reader/src/formats/chm/HuffmanDecoder.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2004-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 "HuffmanDecoder.h" + +HuffmanDecoder::HuffmanDecoder() : myMaxBitsNumber(0) { +} + +void HuffmanDecoder::reset() { + CodeLengths.clear(); +} + +bool HuffmanDecoder::buildTable() { + myMaxBitsNumber = 0; + for (unsigned short symbol = 0; symbol < CodeLengths.size(); symbol++) { + myMaxBitsNumber = std::max(CodeLengths[symbol], myMaxBitsNumber); + } + if (myMaxBitsNumber > 16) { + return false; + } + + unsigned int tableSize = 1 << myMaxBitsNumber; + mySymbols.clear(); + mySymbols.reserve(tableSize); + + for (unsigned char i = 1; i <= myMaxBitsNumber; ++i) { + for (unsigned short symbol = 0; symbol < CodeLengths.size(); symbol++) { + if (CodeLengths[symbol] == i) { + mySymbols.insert(mySymbols.end(), 1 << (myMaxBitsNumber - i), symbol); + if (mySymbols.size() > tableSize) { + return false; + } + } + } + } + + if (mySymbols.size() < tableSize) { + mySymbols.insert(mySymbols.end(), tableSize - mySymbols.size(), 0); + } + + return true; +} diff --git a/reader/src/formats/chm/HuffmanDecoder.h b/reader/src/formats/chm/HuffmanDecoder.h new file mode 100644 index 0000000..bd9f700 --- /dev/null +++ b/reader/src/formats/chm/HuffmanDecoder.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __HUFFMANDECODER_H__ +#define __HUFFMANDECODER_H__ + +#include + +#include "BitStream.h" + +class HuffmanDecoder { + +public: + HuffmanDecoder(); + + bool buildTable(); + void reset(); + + unsigned int getSymbol(BitStream &stream) const; + +private: + unsigned char myMaxBitsNumber; + std::vector mySymbols; + std::vector CodeLengths; + HuffmanDecoder(const HuffmanDecoder&); + const HuffmanDecoder &operator = (const HuffmanDecoder&); + +friend class LZXDecompressor; +}; + +inline unsigned int HuffmanDecoder::getSymbol(BitStream &stream) const { + unsigned int symbol = mySymbols[stream.peek(myMaxBitsNumber)]; + stream.remove(CodeLengths[symbol]); + return symbol; +} + +#endif /* __HUFFMANDECODER_H__ */ diff --git a/reader/src/formats/chm/LZXDecompressor.cpp b/reader/src/formats/chm/LZXDecompressor.cpp new file mode 100644 index 0000000..38b4311 --- /dev/null +++ b/reader/src/formats/chm/LZXDecompressor.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2004-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 "LZXDecompressor.h" + +static unsigned int slotNumber(int windowSizeIndex) { + if (windowSizeIndex == 20) { + return 42; + } else if (windowSizeIndex == 21) { + return 50; + } else { + return 2 * windowSizeIndex; + } +} + +LZXDecompressor::LZXDecompressor(int windowSizeIndex) : myWindow(1 << windowSizeIndex, 0), mySlotNumber(slotNumber(windowSizeIndex)) { + reset(); +} + +void LZXDecompressor::reset() { + myCurrentBlockType = UNKNOWNN; + myReadHeader = true; + + myState.WindowIterator = myWindow.begin(); + myState.R0 = 1; + myState.R1 = 1; + myState.R2 = 1; + + myMainTree.reset(); + myLengthTree.reset(); + + myBlockBytesLeft = 0; + + myE8Decoder.reset(0); +} + +static bool fill(std::vector &data, std::vector::iterator &it, int num, unsigned char value) { + if (data.end() - it < num) { + return false; + } + std::vector::iterator end = it + num; + while (it != end) { + *it++ = value; + } + return true; +} + +bool LZXDecompressor::readLengths(HuffmanDecoder &decoder, std::size_t from, std::size_t size) { + HuffmanDecoder preTree; + preTree.CodeLengths.reserve(20); + for (int i = 0; i < 20; i++) { + preTree.CodeLengths.push_back(myBitStream.get(4)); + } + if (!preTree.buildTable()) { + return false; + } + + std::vector &lengths = decoder.CodeLengths; + if (lengths.size() < from + size) { + lengths.insert(lengths.end(), from + size - lengths.size(), 0); + } + std::vector::iterator start = lengths.begin() + from; + std::vector::iterator end = start + size; + for (std::vector::iterator it = start; it != end; ) { + int z = preTree.getSymbol(myBitStream); + if (z == 17) { + if (!fill(lengths, it, myBitStream.get(4) + 4, 0)) { + return false; + } + } else if (z == 18) { + if (!fill(lengths, it, myBitStream.get(5) + 20, 0)) { + return false; + } + } else if (z == 19) { + unsigned int num = myBitStream.get(1) + 4; + z = *it - preTree.getSymbol(myBitStream); + if (!fill(lengths, it, num, (z < 0) ? z + 17 : z)) { + return false; + } + } else { + z = *it - z; + *it++ = (z < 0) ? z + 17 : z; + } + } + + return true; +} + +static const unsigned int basePosition[51] = { + 0, 1, 2, 3, 4, 6, 8, 12, + 16, 24, 32, 48, 64, 96, 128, 192, + 256, 384, 512, 768, 1024, 1536, 2048, 3072, + 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, + 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, + 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936, + 1835008, 1966080, 2097152 +}; + +bool LZXDecompressor::decodeBytes(DecodingState &state, std::size_t bytesToDecode) { + if (myCurrentBlockType == UNCOMPRESSED) { + if (!myBitStream.getBytesDirect(&*state.WindowIterator, bytesToDecode)) { + return false; + } + state.WindowIterator += bytesToDecode; + return true; + } + + while (bytesToDecode > 0) { + int symbol = myMainTree.getSymbol(myBitStream); + if (symbol < 256) { + *state.WindowIterator++ = symbol; + --bytesToDecode; + continue; + } + + std::size_t length = symbol % 8; + if (length == 7) { + length += myLengthTree.getSymbol(myBitStream); + } + length += 2; + if (length > bytesToDecode) { + return false; + } + + std::size_t offset = (symbol - 256) / 8; + switch (offset) { + case 0: + offset = state.R0; + break; + case 1: + offset = state.R1; + state.R1 = state.R0; + state.R0 = offset; + break; + case 2: + offset = state.R2; + state.R2 = state.R0; + state.R0 = offset; + break; + default: + if ((myCurrentBlockType == VERBATIM) && (offset == 3)) { + offset = 1; + } else { + if (offset > 50) { + return false; + } + const int positionFooterBits = std::max(0, std::min((int)offset / 2 - 1, 17)); + offset = basePosition[offset] - 2; + if ((myCurrentBlockType == VERBATIM) || (positionFooterBits == 1) || (positionFooterBits == 2)) { + offset += myBitStream.get(positionFooterBits); + } else if (positionFooterBits == 3) { + offset += myAlignedOffsetTree.getSymbol(myBitStream); + } else if (positionFooterBits > 3) { + offset += 8 * myBitStream.get(positionFooterBits - 3); + offset += myAlignedOffsetTree.getSymbol(myBitStream); + } else { + offset = 1; + } + } + state.R2 = state.R1; + state.R1 = state.R0; + state.R0 = offset; + break; + } + + if ((state.WindowIterator - myWindow.begin()) + myWindow.size() < offset) { + return false; + } + if (myWindow.size() >= offset + (myWindow.end() - state.WindowIterator)) { + offset += myWindow.size(); + if (myWindow.size() >= offset + (myWindow.end() - state.WindowIterator)) { + return false; + } + } + std::vector::iterator srcIt = state.WindowIterator + (myWindow.size() - offset); + for (std::size_t i = 0; i < length; ++i) { + if (srcIt == myWindow.end()) { + srcIt -= myWindow.size(); + } + *state.WindowIterator++ = *srcIt++; + } + bytesToDecode -= length; + } + return true; +} + +bool LZXDecompressor::decompress(const std::string &data, unsigned char *outBuffer, const std::size_t outSize) { + myBitStream.setData(data); + + if (myReadHeader) { + if (myBitStream.get(1) == 1) { + myE8Decoder.reset(myBitStream.get(32)); + } + myReadHeader = false; + } + + DecodingState state = myState; + + for (std::size_t bytesToWrite = outSize; bytesToWrite > 0; ) { + if (myBlockBytesLeft == 0) { + if (myCurrentBlockType == UNCOMPRESSED) { + if (myBlockSize & 1) { + myBitStream.remove(8); + } + myBitStream.reset(); + } + + myCurrentBlockType = (BlockType)myBitStream.get(3); + myBlockSize = myBitStream.get(24); + myBlockBytesLeft = myBlockSize; + + switch (myCurrentBlockType) { + case UNCOMPRESSED: + myBitStream.reset(); + state.R0 = myBitStream.get4BytesDirect(); + state.R1 = myBitStream.get4BytesDirect(); + state.R2 = myBitStream.get4BytesDirect(); + break; + + case ALIGNED: + myAlignedOffsetTree.CodeLengths.clear(); + for (int i = 0; i < 8; i++) { + myAlignedOffsetTree.CodeLengths.push_back(myBitStream.get(3)); + } + if (!myAlignedOffsetTree.buildTable()) { + return false; + } + // no break; it's not a mistake + + case VERBATIM: + if (!readLengths(myMainTree, 0, 256) || + !readLengths(myMainTree, 256, 8 * mySlotNumber) || + !readLengths(myLengthTree, 0, 249) || + !myMainTree.buildTable() || + !myLengthTree.buildTable()) { + return false; + } + break; + + default: + return false; + } + } + + while ((myBlockBytesLeft > 0) && (bytesToWrite > 0)) { + std::size_t bytesToDecode = std::min(myBlockBytesLeft, bytesToWrite); + if (state.WindowIterator + bytesToDecode > myWindow.end()) { + return false; + } + + if (!decodeBytes(state, bytesToDecode)) { + return false; + } + + bytesToWrite -= bytesToDecode; + myBlockBytesLeft -= bytesToDecode; + } + } + + std::vector::iterator jt = + (state.WindowIterator != myWindow.begin()) ? state.WindowIterator : myWindow.end(); + std::memcpy(outBuffer, &*(jt - outSize), outSize); + + myState = state; + + myE8Decoder.decode(outBuffer, outSize); + + return true; +} diff --git a/reader/src/formats/chm/LZXDecompressor.h b/reader/src/formats/chm/LZXDecompressor.h new file mode 100644 index 0000000..dac9e1f --- /dev/null +++ b/reader/src/formats/chm/LZXDecompressor.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __LZXDECOMPRESSOR_H__ +#define __LZXDECOMPRESSOR_H__ + +#include +#include + +#include "BitStream.h" +#include "HuffmanDecoder.h" + +class LZXDecompressor { + +public: + LZXDecompressor(int windowSizeIndex); + void reset(); + + bool decompress(const std::string &data, unsigned char *outBuffer, const std::size_t outSize); + +private: + struct DecodingState { + std::vector::iterator WindowIterator; + unsigned int R0; + unsigned int R1; + unsigned int R2; + }; + + bool readLengths(HuffmanDecoder &decoder, std::size_t from, std::size_t size); + bool decodeBytes(DecodingState &state, std::size_t bytesToDecode); + +private: + enum BlockType { + UNKNOWNN = 0, + VERBATIM = 1, + ALIGNED = 2, + UNCOMPRESSED = 3 + }; + + BlockType myCurrentBlockType; + bool myReadHeader; + + std::vector myWindow; + + DecodingState myState; + + std::size_t myBlockSize; + std::size_t myBlockBytesLeft; + + const unsigned int mySlotNumber; + HuffmanDecoder myMainTree; + HuffmanDecoder myLengthTree; + HuffmanDecoder myAlignedOffsetTree; + + BitStream myBitStream; + + class E8Decoder { + + public: + void reset(unsigned int fileSize); + void decode(unsigned char *buffer, const std::size_t size); + + private: + unsigned int myFramesCounter; + unsigned int myFileSize; + unsigned int myPosition; + }; + + E8Decoder myE8Decoder; +}; + +#endif /* __LZXDECOMPRESSOR_H__ */ diff --git a/reader/src/formats/css/StyleSheetParser.cpp b/reader/src/formats/css/StyleSheetParser.cpp new file mode 100644 index 0000000..33dc900 --- /dev/null +++ b/reader/src/formats/css/StyleSheetParser.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2004-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 + +#include "StyleSheetParser.h" + +StyleSheetTableParser::StyleSheetTableParser(StyleSheetTable &table) : myTable(table) { + //ZLLogger::Instance().registerClass("CSS"); +} + +void StyleSheetTableParser::storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map) { + std::string s = selector; + ZLStringUtil::stripWhiteSpaces(s); + + if (s.empty()) { + return; + } + + if (s[0] == '@') { + processAtRule(s, map); + return; + } + + const std::vector ids = ZLStringUtil::split(s, ","); + for (std::vector::const_iterator it = ids.begin(); it != ids.end(); ++it) { + std::string id = *it; + ZLStringUtil::stripWhiteSpaces(id); + if (!id.empty()) { + const std::size_t index = id.find('.'); + if (index == std::string::npos) { + myTable.addMap(id, std::string(), map); + } else { + myTable.addMap(id.substr(0, index), id.substr(index + 1), map); + } + } + } +} + +void StyleSheetTableParser::processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map) { + (void)map; + if (name == "@font-face") { + } +} + +shared_ptr StyleSheetSingleStyleParser::parseString(const char *text) { + myReadState = WAITING_FOR_ATTRIBUTE; + parse(text, std::strlen(text), true); + shared_ptr control = StyleSheetTable::createControl(myMap); + reset(); + return control; +} + +StyleSheetParser::StyleSheetParser() { + reset(); +} + +StyleSheetParser::~StyleSheetParser() { +} + +void StyleSheetParser::reset() { + myWord.erase(); + myAttributeName.erase(); + myReadState = WAITING_FOR_SELECTOR; + myInsideComment = false; + mySelectorString.erase(); + myMap.clear(); +} + +void StyleSheetParser::parse(ZLInputStream &stream) { + if (stream.open()) { + char *buffer = new char[1024]; + while (true) { + int len = stream.read(buffer, 1024); + if (len == 0) { + break; + } + parse(buffer, len); + } + delete[] buffer; + stream.close(); + } +} + +void StyleSheetParser::parse(const char *text, int len, bool final) { + const char *start = text; + const char *end = text + len; + for (const char *ptr = start; ptr != end; ++ptr) { + if (std::isspace(*ptr)) { + if (start != ptr) { + myWord.append(start, ptr - start); + } + processWord(myWord); + myWord.erase(); + start = ptr + 1; + } else if (isControlSymbol(*ptr)) { + if (start != ptr) { + myWord.append(start, ptr - start); + } + processWord(myWord); + myWord.erase(); + processControl(*ptr); + start = ptr + 1; + } + } + if (start < end) { + myWord.append(start, end - start); + if (final) { + processWord(myWord); + myWord.erase(); + } + } +} + +bool StyleSheetParser::isControlSymbol(const char symbol) { + switch (myReadState) { + default: + case WAITING_FOR_SELECTOR: + return false; + case SELECTOR: + return symbol == '{' || symbol == ';'; + case WAITING_FOR_ATTRIBUTE: + return symbol == '}' || symbol == ':'; + case ATTRIBUTE_NAME: + return symbol == ':'; + case ATTRIBUTE_VALUE: + return symbol == '}' || symbol == ';'; + } +} + +void StyleSheetParser::storeData(const std::string&, const StyleSheetTable::AttributeMap&) { +} + +void StyleSheetParser::processAtRule(const std::string&, const StyleSheetTable::AttributeMap&) { +} + +void StyleSheetParser::processControl(const char control) { + switch (myReadState) { + case WAITING_FOR_SELECTOR: + break; + case SELECTOR: + switch (control) { + case '{': + myReadState = WAITING_FOR_ATTRIBUTE; + break; + case ';': + myReadState = WAITING_FOR_SELECTOR; + mySelectorString.erase(); + break; + } + break; + case WAITING_FOR_ATTRIBUTE: + if (control == '}') { + myReadState = WAITING_FOR_SELECTOR; + storeData(mySelectorString, myMap); + mySelectorString.erase(); + myMap.clear(); + } + break; + case ATTRIBUTE_NAME: + if (control == ':') { + myReadState = ATTRIBUTE_VALUE; + } + break; + case ATTRIBUTE_VALUE: + if (control == ';') { + myReadState = WAITING_FOR_ATTRIBUTE; + } else if (control == '}') { + myReadState = WAITING_FOR_SELECTOR; + storeData(mySelectorString, myMap); + mySelectorString.erase(); + myMap.clear(); + } + break; + } +} + +void StyleSheetParser::processWord(std::string &word) { + while (!word.empty()) { + int index = word.find(myInsideComment ? "*/" : "/*"); + if (!myInsideComment) { + if (index == -1) { + processWordWithoutComments(word); + } else if (index > 0) { + processWordWithoutComments(word.substr(0, index)); + } + } + if (index == -1) { + break; + } + myInsideComment = !myInsideComment; + word.erase(0, index + 2); + } +} + +void StyleSheetParser::processWordWithoutComments(const std::string &word) { + switch (myReadState) { + case WAITING_FOR_SELECTOR: + myReadState = SELECTOR; + mySelectorString = word; + break; + case SELECTOR: + mySelectorString += ' ' + word; + break; + case WAITING_FOR_ATTRIBUTE: + myReadState = ATTRIBUTE_NAME; + // go through + case ATTRIBUTE_NAME: + myAttributeName = word; + myMap[myAttributeName].clear(); + break; + case ATTRIBUTE_VALUE: + { + const std::size_t l = word.length(); + if (l >= 2 && (word[0] == '"' || word[0] == '\'') && word[0] == word[l - 1]) { + myMap[myAttributeName].push_back(word.substr(1, l - 2)); + } else { + myMap[myAttributeName].push_back(word); + } + break; + } + } +} diff --git a/reader/src/formats/css/StyleSheetParser.h b/reader/src/formats/css/StyleSheetParser.h new file mode 100644 index 0000000..8949823 --- /dev/null +++ b/reader/src/formats/css/StyleSheetParser.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __STYLESHEETPARSER_H__ +#define __STYLESHEETPARSER_H__ + +#include "StyleSheetTable.h" + +class ZLInputStream; + +class StyleSheetParser { + +protected: + StyleSheetParser(); + +public: + virtual ~StyleSheetParser(); + void reset(); + void parse(ZLInputStream &stream); + void parse(const char *text, int len, bool final = false); + +protected: + virtual void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map); + virtual void processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map); + +private: + bool isControlSymbol(const char symbol); + void processWord(std::string &word); + void processWordWithoutComments(const std::string &word); + void processControl(const char control); + +private: + std::string myWord; + std::string myAttributeName; + enum { + WAITING_FOR_SELECTOR, + SELECTOR, + WAITING_FOR_ATTRIBUTE, + ATTRIBUTE_NAME, + ATTRIBUTE_VALUE, + } myReadState; + bool myInsideComment; + std::string mySelectorString; + StyleSheetTable::AttributeMap myMap; + +friend class StyleSheetSingleStyleParser; +}; + +class StyleSheetTableParser : public StyleSheetParser { + +public: + StyleSheetTableParser(StyleSheetTable &table); + +private: + void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map); + void processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map); + +private: + StyleSheetTable &myTable; +}; + +class StyleSheetSingleStyleParser : public StyleSheetParser { + +public: + shared_ptr parseString(const char *text); +}; + +#endif /* __STYLESHEETPARSER_H__ */ diff --git a/reader/src/formats/css/StyleSheetTable.cpp b/reader/src/formats/css/StyleSheetTable.cpp new file mode 100644 index 0000000..fe45a85 --- /dev/null +++ b/reader/src/formats/css/StyleSheetTable.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2004-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 "StyleSheetTable.h" + +bool StyleSheetTable::isEmpty() const { + return myControlMap.empty() && myPageBreakBeforeMap.empty() && myPageBreakAfterMap.empty(); +} + +void StyleSheetTable::addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map) { + if ((!tag.empty() || !aClass.empty()) && !map.empty()) { + Key key(tag, aClass); + myControlMap[key] = createControl(map); + const std::vector &pbb = values(map, "page-break-before"); + if (!pbb.empty()) { + if ((pbb[0] == "always") || + (pbb[0] == "left") || + (pbb[0] == "right")) { + myPageBreakBeforeMap[key] = true; + } else if (pbb[0] == "avoid") { + myPageBreakBeforeMap[key] = false; + } + } + const std::vector &pba = values(map, "page-break-after"); + if (!pba.empty()) { + if ((pba[0] == "always") || + (pba[0] == "left") || + (pba[0] == "right")) { + myPageBreakAfterMap[key] = true; + } else if (pba[0] == "avoid") { + myPageBreakAfterMap[key] = false; + } + } + } +} + +static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) { + if (ZLStringUtil::stringEndsWith(toParse, "%")) { + unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; + size = std::atoi(toParse.c_str()); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "em")) { + unit = ZLTextStyleEntry::SIZE_UNIT_EM_100; + size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0)); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "ex")) { + unit = ZLTextStyleEntry::SIZE_UNIT_EX_100; + size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0)); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "px")) { + unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL; + size = std::atoi(toParse.c_str()); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "pt")) { + unit = ZLTextStyleEntry::SIZE_UNIT_POINT; + size = std::atoi(toParse.c_str()); + return true; + } + return false; +} + +void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Feature featureId, const AttributeMap &map, const std::string &attributeName) { + StyleSheetTable::AttributeMap::const_iterator it = map.find(attributeName); + if (it == map.end()) { + return; + } + const std::vector &values = it->second; + if (!values.empty() && !values[0].empty()) { + short size; + ZLTextStyleEntry::SizeUnit unit; + if (parseLength(values[0], size, unit)) { + entry.setLength(featureId, size, unit); + } + } +} + +bool StyleSheetTable::doBreakBefore(const std::string &tag, const std::string &aClass) const { + std::map::const_iterator it = myPageBreakBeforeMap.find(Key(tag, aClass)); + if (it != myPageBreakBeforeMap.end()) { + return it->second; + } + + it = myPageBreakBeforeMap.find(Key("", aClass)); + if (it != myPageBreakBeforeMap.end()) { + return it->second; + } + + it = myPageBreakBeforeMap.find(Key(tag, "")); + if (it != myPageBreakBeforeMap.end()) { + return it->second; + } + + return false; +} + +bool StyleSheetTable::doBreakAfter(const std::string &tag, const std::string &aClass) const { + std::map::const_iterator it = myPageBreakAfterMap.find(Key(tag, aClass)); + if (it != myPageBreakAfterMap.end()) { + return it->second; + } + + it = myPageBreakAfterMap.find(Key("", aClass)); + if (it != myPageBreakAfterMap.end()) { + return it->second; + } + + it = myPageBreakAfterMap.find(Key(tag, "")); + if (it != myPageBreakAfterMap.end()) { + return it->second; + } + + return false; +} + +shared_ptr StyleSheetTable::control(const std::string &tag, const std::string &aClass) const { + std::map >::const_iterator it = + myControlMap.find(Key(tag, aClass)); + return (it != myControlMap.end()) ? it->second : 0; +} + +const std::vector &StyleSheetTable::values(const AttributeMap &map, const std::string &name) { + const AttributeMap::const_iterator it = map.find(name); + if (it != map.end()) { + return it->second; + } + static const std::vector emptyVector; + return emptyVector; +} + +shared_ptr StyleSheetTable::createControl(const AttributeMap &styles) { + shared_ptr entry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_CSS_ENTRY); + + const std::vector &alignment = values(styles, "text-align"); + if (!alignment.empty()) { + if (alignment[0] == "justify") { + entry->setAlignmentType(ALIGN_JUSTIFY); + } else if (alignment[0] == "left") { + entry->setAlignmentType(ALIGN_LEFT); + } else if (alignment[0] == "right") { + entry->setAlignmentType(ALIGN_RIGHT); + } else if (alignment[0] == "center") { + entry->setAlignmentType(ALIGN_CENTER); + } + } + + const std::vector &deco = values(styles, "text-decoration"); + for (std::vector::const_iterator it = deco.begin(); it != deco.end(); ++it) { + if (*it == "underline") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_UNDERLINED, true); + } else if (*it == "line-through") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_STRIKEDTHROUGH, true); + } else if (*it == "none") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_UNDERLINED, false); + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_STRIKEDTHROUGH, false); + } + } + + const std::vector &bold = values(styles, "font-weight"); + if (!bold.empty()) { + //ZLLogger::Instance().println(ZLLogger::DEFAULT_CLASS, "bold: " + bold[0]); + int num = -1; + if (bold[0] == "bold") { + num = 700; + } else if (bold[0] == "normal") { + num = 400; + } else if (bold[0] == "bolder") { + // TODO: implement + } else if (bold[0] == "lighter") { + // TODO: implement + } else { + num = ZLStringUtil::stringToInteger(bold[0], -1); + } + if (num != -1) { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_BOLD, num >= 600); + } + } + + const std::vector &italic = values(styles, "font-style"); + if (!italic.empty()) { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_ITALIC, italic[0] == "italic"); + } + + const std::vector &variant = values(styles, "font-variant"); + if (!variant.empty()) { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps"); + } + + const std::vector &fontFamily = values(styles, "font-family"); + if (!fontFamily.empty() && !fontFamily[0].empty()) { + entry->setFontFamily(fontFamily[0]); + //ZLLogger::Instance().println(ZLLogger::DEFAULT_CLASS, "font family: " + fontFamily[0]); + } + + const std::vector &fontSize = values(styles, "font-size"); + if (!fontSize.empty()) { + //TODO implement FONT_MODIFIER_INHERIT, SMALLER and LARGER support + bool doSetFontSize = true; + short size = 100; + ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; + if (fontSize[0] == "xx-small") { + size = 58; + } else if (fontSize[0] == "x-small") { + size = 69; + } else if (fontSize[0] == "small") { + size = 83; + } else if (fontSize[0] == "medium") { + size = 100; + } else if (fontSize[0] == "large") { + size = 120; + } else if (fontSize[0] == "x-large") { + size = 144; + } else if (fontSize[0] == "xx-large") { + size = 173; + } else if (fontSize[0] == "inherit") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_INHERIT, true); + doSetFontSize = false; + } else if (fontSize[0] == "smaller") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLER, true); + doSetFontSize = false; + } else if (fontSize[0] == "larger") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_LARGER, true); + doSetFontSize = false; + } else if (!parseLength(fontSize[0], size, unit)) { + doSetFontSize = false; + } + if (doSetFontSize) { + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, size, unit); + } + } + + setLength(*entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left"); + setLength(*entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right"); + setLength(*entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent"); + setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top"); + setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top"); + setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom"); + setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom"); + + return entry; +} + +void StyleSheetTable::clear() { + myControlMap.clear(); + myPageBreakBeforeMap.clear(); + myPageBreakAfterMap.clear(); +} diff --git a/reader/src/formats/css/StyleSheetTable.h b/reader/src/formats/css/StyleSheetTable.h new file mode 100644 index 0000000..54236fb --- /dev/null +++ b/reader/src/formats/css/StyleSheetTable.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __STYLESHEETTABLE_H__ +#define __STYLESHEETTABLE_H__ + +#include +#include +#include + +#include + +#include +#include + +class StyleSheetTable { + +public: + typedef std::map > AttributeMap; + static shared_ptr createControl(const AttributeMap &map); + +private: + void addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map); + + static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Feature featureId, const AttributeMap &map, const std::string &attributeName); + static const std::vector &values(const AttributeMap &map, const std::string &name); + +public: + bool isEmpty() const; + bool doBreakBefore(const std::string &tag, const std::string &aClass) const; + bool doBreakAfter(const std::string &tag, const std::string &aClass) const; + shared_ptr control(const std::string &tag, const std::string &aClass) const; + + void clear(); + +private: + struct Key { + Key(const std::string &tag, const std::string &aClass); + + const std::string TagName; + const std::string ClassName; + + bool operator < (const Key &key) const; + }; + + std::map > myControlMap; + std::map myPageBreakBeforeMap; + std::map myPageBreakAfterMap; + +friend class StyleSheetTableParser; +}; + +inline StyleSheetTable::Key::Key(const std::string &tag, const std::string &aClass) : TagName(tag), ClassName(aClass) { +} + +inline bool StyleSheetTable::Key::operator < (const StyleSheetTable::Key &key) const { + return (TagName < key.TagName) || ((TagName == key.TagName) && (ClassName < key.ClassName)); +} + +#endif /* __STYLESHEETTABLE_H__ */ diff --git a/reader/src/formats/doc/DocBookReader.cpp b/reader/src/formats/doc/DocBookReader.cpp new file mode 100644 index 0000000..99f471a --- /dev/null +++ b/reader/src/formats/doc/DocBookReader.cpp @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2004-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 +#include +#include + +#include "DocBookReader.h" +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +#include "OleStorage.h" +#include "OleMainStream.h" + +DocBookReader::DocBookReader(BookModel &model, const std::string &encoding) : + myModelReader(model), + myPictureCounter(0), + myEncoding(encoding) { + myReadState = READ_TEXT; +} + +bool DocBookReader::readBook() { + const ZLFile &file = myModelReader.model().book()->file(); + shared_ptr stream = file.inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + myModelReader.setMainTextModel(); + myModelReader.pushKind(REGULAR); + myModelReader.beginParagraph(); + + if (!readDocument(stream, true)) { + return false; + } + + myModelReader.insertEndOfTextParagraph(); + return true; +} + +void DocBookReader::handleChar(ZLUnicodeUtil::Ucs2Char ucs2char) { + if (myReadState == READ_FIELD && myReadFieldState == READ_FIELD_INFO) { + myFieldInfoBuffer.push_back(ucs2char); + return; + } + if (myReadState == READ_FIELD && myReadFieldState == DONT_READ_FIELD_TEXT) { + return; + } + if (myReadState == READ_FIELD && myReadFieldState == READ_FIELD_TEXT && ucs2char == WORD_HORIZONTAL_TAB) { + //to remove pagination from TOC (from doc saved in OpenOffice) + myReadFieldState = DONT_READ_FIELD_TEXT; + return; + } + std::string utf8String; + ZLUnicodeUtil::Ucs2String ucs2String; + ucs2String.push_back(ucs2char); + ZLUnicodeUtil::ucs2ToUtf8(utf8String, ucs2String); + if (!myModelReader.paragraphIsOpen()) { + myModelReader.beginParagraph(); + } + myModelReader.addData(utf8String); +} + +void DocBookReader::handleHardLinebreak() { + if (myModelReader.paragraphIsOpen()) { + myModelReader.endParagraph(); + } + myModelReader.beginParagraph(); + if (!myCurrentStyleEntry.isNull()) { + myModelReader.addStyleEntry(*myCurrentStyleEntry); + } + for (std::size_t i = 0; i < myKindStack.size(); ++i) { + myModelReader.addControl(myKindStack.at(i), true); + } +} + +void DocBookReader::handleParagraphEnd() { + if (myModelReader.paragraphIsOpen()) { + myModelReader.endParagraph(); + } + myModelReader.beginParagraph(); + myCurrentStyleEntry = 0; +} + +void DocBookReader::handlePageBreak() { + if (myModelReader.paragraphIsOpen()) { + myModelReader.endParagraph(); + } + myCurrentStyleEntry = 0; + myModelReader.insertEndOfSectionParagraph(); + myModelReader.beginParagraph(); +} + +void DocBookReader::handleTableSeparator() { + handleChar(SPACE); + handleChar(VERTICAL_LINE); + handleChar(SPACE); +} + +void DocBookReader::handleTableEndRow() { + handleParagraphEnd(); +} + +void DocBookReader::handleFootNoteMark() { + //TODO implement +} + +void DocBookReader::handleStartField() { + if (myReadState == READ_FIELD) { //for nested fields + handleEndField(); + } + myReadState = READ_FIELD; + myReadFieldState = READ_FIELD_INFO; + myHyperlinkTypeState = NO_HYPERLINK; +} + +void DocBookReader::handleSeparatorField() { + static const std::string HYPERLINK = "HYPERLINK"; + static const std::string SEQUENCE = "SEQ"; +// static const std::string PAGE = "PAGE"; +// static const std::string PAGEREF = "PAGEREF"; +// static const std::string SHAPE = "SHAPE"; + static const std::string SPACE_DELIMETER = " "; + static const std::string LOCAL_LINK = "\\l"; + static const std::string QUOTE = "\""; + myReadFieldState = READ_FIELD_TEXT; + myHyperlinkTypeState = NO_HYPERLINK; + ZLUnicodeUtil::Ucs2String buffer = myFieldInfoBuffer; + myFieldInfoBuffer.clear(); + std::string utf8String; + ZLUnicodeUtil::ucs2ToUtf8(utf8String, buffer); + ZLUnicodeUtil::utf8Trim(utf8String); + if (utf8String.empty()) { + return; + } + std::vector result = ZLStringUtil::split(utf8String, SPACE_DELIMETER); + //TODO split function can returns empty string, maybe fix it + std::vector splitted; + for (std::size_t i = 0; i < result.size(); ++i) { + if (!result.at(i).empty()) { + splitted.push_back(result.at(i)); + } + } + + if (!splitted.empty() && splitted.at(0) == SEQUENCE) { + myReadFieldState = READ_FIELD_TEXT; + myHyperlinkTypeState = NO_HYPERLINK; + return; + } + + if (splitted.size() < 2 || splitted.at(0) != HYPERLINK) { + myReadFieldState = DONT_READ_FIELD_TEXT; + //to remove pagination from TOC and not hyperlink fields + return; + } + + if (splitted.at(1) == LOCAL_LINK) { + std::string link = parseLink(buffer); + if (!link.empty()) { + myModelReader.addHyperlinkControl(INTERNAL_HYPERLINK, link); + myHyperlinkTypeState = INT_HYPERLINK_INSERTED; + } + } else { + std::string link = parseLink(buffer, true); + if (!link.empty()) { + myModelReader.addHyperlinkControl(EXTERNAL_HYPERLINK, link); + myHyperlinkTypeState = EXT_HYPERLINK_INSERTED; + } + } +} + +void DocBookReader::handleEndField() { + myFieldInfoBuffer.clear(); + if (myReadState == READ_TEXT) { + return; + } + if (myHyperlinkTypeState == EXT_HYPERLINK_INSERTED) { + myModelReader.addControl(EXTERNAL_HYPERLINK, false); + } else if (myHyperlinkTypeState == INT_HYPERLINK_INSERTED) { + myModelReader.addControl(INTERNAL_HYPERLINK, false); + } + myReadState = READ_TEXT; + myHyperlinkTypeState = NO_HYPERLINK; + +} + +void DocBookReader::handleImage(const ZLFileImage::Blocks &blocks) { + std::string number; + ZLStringUtil::appendNumber(number, myPictureCounter++); + myModelReader.addImageReference(number); + ZLFile file(myModelReader.model().book()->file().path(), ZLMimeType::IMAGE_AUTO); + myModelReader.addImage(number, new ZLFileImage(file, blocks, ZLFileImage::ENCODING_NONE)); +} + +void DocBookReader::handleOtherControlChar(ZLUnicodeUtil::Ucs2Char ucs2char) { + if (ucs2char == WORD_MINUS) { + handleChar(MINUS); + } else if (ucs2char == WORD_SOFT_HYPHEN) { + //skip + } else if (ucs2char == WORD_HORIZONTAL_TAB) { + handleChar(ucs2char); + } else { +// myTextBuffer.clear(); + } +} + +void DocBookReader::handleFontStyle(unsigned int fontStyle) { + if (myReadState == READ_FIELD && myReadFieldState == READ_FIELD_TEXT && myHyperlinkTypeState != NO_HYPERLINK) { + //to fix bug with hyperlink, that's only bold and doesn't looks like hyperlink + return; + } + while (!myKindStack.empty()) { + myModelReader.addControl(myKindStack.back(), false); + myKindStack.pop_back(); + } + if (fontStyle & OleMainStream::CharInfo::FONT_BOLD) { + myKindStack.push_back(BOLD); + } + if (fontStyle & OleMainStream::CharInfo::FONT_ITALIC) { + myKindStack.push_back(ITALIC); + } + for (std::size_t i = 0; i < myKindStack.size(); ++i) { + myModelReader.addControl(myKindStack.at(i), true); + } +} + +void DocBookReader::handleParagraphStyle(const OleMainStream::Style &styleInfo) { + if (styleInfo.HasPageBreakBefore) { + handlePageBreak(); + } + shared_ptr entry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + + switch (styleInfo.Alignment) { + default: // in that case, use default alignment type + break; + case OleMainStream::Style::ALIGNMENT_LEFT: + entry->setAlignmentType(ALIGN_LEFT); + break; + case OleMainStream::Style::ALIGNMENT_RIGHT: + entry->setAlignmentType(ALIGN_RIGHT); + break; + case OleMainStream::Style::ALIGNMENT_CENTER: + entry->setAlignmentType(ALIGN_CENTER); + break; + case OleMainStream::Style::ALIGNMENT_JUSTIFY: + entry->setAlignmentType(ALIGN_JUSTIFY); + break; + } + + //TODO in case, where style is heading, but size is small it works wrong + const ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; + switch (styleInfo.StyleIdCurrent) { + default: + break; + case OleMainStream::Style::STYLE_H1: + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, 140, unit); + break; + case OleMainStream::Style::STYLE_H2: + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, 120, unit); + break; + case OleMainStream::Style::STYLE_H3: + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, 110, unit); + break; + } + myCurrentStyleEntry = entry; + myModelReader.addStyleEntry(*myCurrentStyleEntry); + + // we should have the same font style, as for the previous paragraph, + // if it has the same StyleIdCurrent + if (myCurrentStyleInfo.StyleIdCurrent != OleMainStream::Style::STYLE_INVALID && + myCurrentStyleInfo.StyleIdCurrent == styleInfo.StyleIdCurrent) { + for (std::size_t i = 0; i < myKindStack.size(); ++i) { + myModelReader.addControl(myKindStack.at(i), true); + } + } else { + myKindStack.clear(); + // fill by the fontstyle, that was got from Stylesheet + handleFontStyle(styleInfo.CurrentCharInfo.FontStyle); + } + myCurrentStyleInfo = styleInfo; +} + +void DocBookReader::handleBookmark(const std::string &name) { + myModelReader.addHyperlinkLabel(name); +} + +std::string DocBookReader::parseLink(ZLUnicodeUtil::Ucs2String s, bool urlencode) { + //TODO add support for HYPERLINK like that: + // [0x13] HYPERLINK "http://site.ru/some text" \t "_blank" [0x14] text [0x15] + //Current implementation search for last QUOTE, so, it reads \t and _blank as part of link + //Last quote searching is need to handle link like that: + // [0x13] HYPERLINK "http://yandex.ru/yandsearch?text='some text' и "some text2"" [0x14] link text [0x15] + + static const ZLUnicodeUtil::Ucs2Char QUOTE = 0x22; + std::size_t i, first = 0; + //TODO maybe functions findFirstOf and findLastOf should be in ZLUnicodeUtil class + for (i = 0; i < s.size(); ++i) { + if (s.at(i) == QUOTE) { + first = i; + break; + } + } + if (i == s.size()) { + return std::string(); + } + std::size_t j, last = 0; + for (j = s.size(); j > 0 ; --j) { + if (s.at(j - 1) == QUOTE) { + last = j - 1; + break; + } + } + if (j == 0 || last == first) { + return std::string(); + } + + ZLUnicodeUtil::Ucs2String link; + for (std::size_t k = first + 1; k < last; ++k) { + ZLUnicodeUtil::Ucs2Char ch = s.at(k); + if (urlencode && ZLUnicodeUtil::isSpace(ch)) { + //TODO maybe implement function for encoding all signs in url, not only spaces and quotes + //TODO maybe add backslash support + link.push_back('%'); + link.push_back('2'); + link.push_back('0'); + } else if (urlencode && ch == QUOTE) { + link.push_back('%'); + link.push_back('2'); + link.push_back('2'); + } else { + link.push_back(ch); + } + } + std::string utf8String; + ZLUnicodeUtil::ucs2ToUtf8(utf8String, link); + return utf8String; +} + +void DocBookReader::footnotesStartHandler() { + handlePageBreak(); +} + +void DocBookReader::ansiDataHandler(const char *buffer, std::size_t len) { + if (myConverter.isNull()) { + // lazy converter initialization + ZLEncodingCollection &collection = ZLEncodingCollection::Instance(); + ZLEncodingConverterInfoPtr info = collection.info(myEncoding); + myConverter = info.isNull() ? collection.defaultConverter() : info->createConverter(); + } + std::string utf8String; + myConverter->convert(utf8String, buffer, buffer + len); + ZLUnicodeUtil::utf8ToUcs2(myBuffer, utf8String); +} + +void DocBookReader::ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol) { + myBuffer.push_back(symbol); +} diff --git a/reader/src/formats/doc/DocBookReader.h b/reader/src/formats/doc/DocBookReader.h new file mode 100644 index 0000000..d80fb8e --- /dev/null +++ b/reader/src/formats/doc/DocBookReader.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __DOCBOOKREADER_H__ +#define __DOCBOOKREADER_H__ + +#include + +#include +#include +#include +#include + +#include "../../bookmodel/BookReader.h" + +#include "OleMainStream.h" +#include "OleStreamParser.h" + +class DocBookReader : public OleStreamParser { + +public: + DocBookReader(BookModel &model, const std::string &encoding); + ~DocBookReader(); + bool readBook(); + +private: + void ansiDataHandler(const char *buffer, std::size_t len); + void ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol); + void footnotesStartHandler(); + + void handleChar(ZLUnicodeUtil::Ucs2Char ucs2char); + void handleHardLinebreak(); + void handleParagraphEnd(); + void handlePageBreak(); + void handleTableSeparator(); + void handleTableEndRow(); + void handleFootNoteMark(); + void handleStartField(); + void handleSeparatorField(); + void handleEndField(); + void handleImage(const ZLFileImage::Blocks &blocks); + void handleOtherControlChar(ZLUnicodeUtil::Ucs2Char ucs2char); + + //formatting: + void handleFontStyle(unsigned int fontStyle); + void handleParagraphStyle(const OleMainStream::Style &styleInfo); + void handleBookmark(const std::string &name); + +private: + static std::string parseLink(ZLUnicodeUtil::Ucs2String s, bool urlencode = false); + +private: + BookReader myModelReader; + + ZLUnicodeUtil::Ucs2String myFieldInfoBuffer; + + enum { + READ_FIELD, + READ_TEXT + } myReadState; + + enum { + READ_FIELD_TEXT, + DONT_READ_FIELD_TEXT, + READ_FIELD_INFO + } myReadFieldState; + + //maybe it should be flag? + enum { + NO_HYPERLINK, + EXT_HYPERLINK_INSERTED, + INT_HYPERLINK_INSERTED + } myHyperlinkTypeState; + + //formatting + std::vector myKindStack; + shared_ptr myCurrentStyleEntry; + OleMainStream::Style myCurrentStyleInfo; + unsigned int myPictureCounter; + + const std::string myEncoding; + shared_ptr myConverter; +}; + +inline DocBookReader::~DocBookReader() {} + +#endif /* __DOCBOOKREADER_H__ */ diff --git a/reader/src/formats/doc/DocFloatImageReader.cpp b/reader/src/formats/doc/DocFloatImageReader.cpp new file mode 100644 index 0000000..8c308e4 --- /dev/null +++ b/reader/src/formats/doc/DocFloatImageReader.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2004-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 "OleUtil.h" +#include "OleStream.h" +#include "OleMainStream.h" + +#include "DocFloatImageReader.h" + +DocFloatImageReader::DocFloatImageReader(unsigned int off, unsigned int len, shared_ptr tableStream, shared_ptr mainStream) : + myTableStream(tableStream), + myMainStream(mainStream), + myOffset(off), + myLength(len) { +} + +void DocFloatImageReader::readAll() { + //OfficeArtContent structure is described at p.405-406 [MS-DOC] + if (!myTableStream->seek(myOffset, true)) { + ZLLogger::Instance().println("DocPlugin", "problems with reading float images"); + return; + } + + unsigned int count = 0; + + RecordHeader header; + while (count < myLength) { + count += readRecordHeader(header, myTableStream); + switch (header.type) { + case 0xF000: + count += readDggContainer(myItem, header.length, myTableStream, myMainStream); + break; + case 0xF002: + count += readDgContainer(myItem, header.length, myTableStream); + break; + default: + return; + break; + } + } +} + +ZLFileImage::Blocks DocFloatImageReader::getBlocksForShapeId(unsigned int shapeId) const { + FSPContainer container; + bool found = false; + for (std::size_t i = 0; !found && i < myItem.FSPs.size(); ++i) { + if (myItem.FSPs.at(i).fsp.shapeId == shapeId) { + found = true; + container = myItem.FSPs.at(i); + } + } + + if (!found || container.fopte.empty()) { + return ZLFileImage::Blocks(); + } + + for (std::size_t i = 0; i < container.fopte.size(); ++i) { + const FOPTE &fopte = container.fopte.at(i); + if (fopte.pId == 0x0104 && !fopte.isComplex) { //0x0104 specifies the BLIP, see p.420 [MS-ODRAW] + if (fopte.value <= myItem.blips.size() && fopte.value > 0) { + Blip blip = myItem.blips.at(fopte.value - 1); + return blip.blocks; + } + } + } + return ZLFileImage::Blocks(); +} + +unsigned int DocFloatImageReader::readRecordHeader(RecordHeader &header, shared_ptr stream) { + //OfficeArtRecordHeader structure is described at p.26 [MS-ODRAW] + char buffer[8]; + stream->read(buffer, 8); + unsigned int temp = OleUtil::getU2Bytes(buffer, 0); + header.version = temp & 0x000F; + header.instance = temp >> 4; + header.type = OleUtil::getU2Bytes(buffer, 2); + header.length = OleUtil::getU4Bytes(buffer, 4); + return 8; +} + +unsigned int DocFloatImageReader::readDggContainer(OfficeArtContent &item, unsigned int length, shared_ptr stream, shared_ptr mainStream) { + //OfficeArtDggContainer structure is described at p.50 [MS-ODRAW] + RecordHeader header; + unsigned int count = 0; + + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF001: + count += readBStoreContainer(item, header.length, stream, mainStream); + break; + default: + count += skipRecord(header, stream); + break; + } + } + + stream->seek(1, false); //skipping dgglbl (see p.406 [MS-DOC]) + ++count; + + return count; +} + +unsigned int DocFloatImageReader::readBStoreContainer(OfficeArtContent &item, unsigned int length, shared_ptr stream, shared_ptr mainStream) { + //OfficeArtBStoreContainer structure is described at p.58 [MS-ODRAW] + RecordHeader header; + unsigned int count = 0; + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF007: + { + Blip blip; + count += readBStoreContainerFileBlock(blip, stream, mainStream); + item.blips.push_back(blip); + } + break; + default: + count += skipRecord(header, stream); + break; + } + } + return count; +} + +unsigned int DocFloatImageReader::skipRecord(const RecordHeader &header, shared_ptr stream) { + stream->seek(header.length, false); + return header.length; +} + +unsigned int DocFloatImageReader::readBStoreContainerFileBlock(Blip &blip, shared_ptr stream, shared_ptr mainStream) { + //OfficeArtBStoreContainerFileBlock structure is described at p.59 [MS-ODRAW] + unsigned int count = readFBSE(blip.storeEntry, stream); + if (blip.storeEntry.offsetInDelay != (unsigned int)-1) { + if (mainStream->seek(blip.storeEntry.offsetInDelay, true)) { //see p.70 [MS-ODRAW] + //TODO maybe we should stop reading float images here + ZLLogger::Instance().println("DocPlugin", "DocFloatImageReader: problems with seeking for offset"); + return count; + } + } + RecordHeader header; + unsigned int count2 = readRecordHeader(header, mainStream); + switch (header.type) { + case OleMainStream::IMAGE_WMF: + case OleMainStream::IMAGE_EMF: + case OleMainStream::IMAGE_PICT: + count2 += skipRecord(header, mainStream); + break; + case OleMainStream::IMAGE_JPEG: + case OleMainStream::IMAGE_JPEG2: + case OleMainStream::IMAGE_PNG: + case OleMainStream::IMAGE_DIB: + case OleMainStream::IMAGE_TIFF: + count2 += readBlip(blip, header, mainStream); + break; + } + blip.type = header.type; + return count; +} + +unsigned int DocFloatImageReader::readBlip(Blip &blip, const RecordHeader &header, shared_ptr stream) { + //OfficeArtBlip structure is described at p.60-66 [MS-ODRAW] + stream->seek(16, false); //skipping rgbUid1 + unsigned int count = 16; + + bool addField = false; + switch (header.type) { + case OleMainStream::IMAGE_PNG: + if (header.instance == 0x6E1) { + addField = true; + } + break; + case OleMainStream::IMAGE_JPEG: + case OleMainStream::IMAGE_JPEG2: + if (header.instance == 0x46B || header.instance == 0x6E3) { + addField = true; + } + break; + case OleMainStream::IMAGE_DIB: + if (header.instance == 0x7A9) { + addField = true; + } + case OleMainStream::IMAGE_TIFF: + if (header.instance == 0x6E5) { + addField = true; + } + break; + } + + if (addField) { + stream->seek(16, false); //skipping rgbUid2 + count += 16; + } + stream->seek(1, false); //skipping tag + count += 1; + + blip.blocks = stream->getBlockPieceInfoList(stream->offset(), header.length - count); + count += header.length; + return count; +} + +unsigned int DocFloatImageReader::readFBSE(BlipStoreEntry &fbse, shared_ptr stream) { + //OfficeArtFBSE structure is described at p.68 [MS-ODRAW] + stream->seek(2, false); //skipping btWin32 and btMacOS + stream->seek(16, false); //skipping rgbUid + stream->seek(2, false); //skipping tag + fbse.size = read4Bytes(stream); + fbse.referenceCount = read4Bytes(stream); + fbse.offsetInDelay = read4Bytes(stream); + stream->seek(1, false); //skipping unused value + unsigned int lengthName = read1Byte(stream); //if it should be multiplied on 2? + stream->seek(2, false); // skipping unused values + if (lengthName > 0) { + stream->seek(lengthName, false); //skipping nameData + } + return 36 + lengthName; +} + +unsigned int DocFloatImageReader::readDgContainer(OfficeArtContent &item, unsigned int length, shared_ptr stream) { + //OfficeArtDgContainer structure is described at p.52 [MS-ODRAW] + unsigned int count = 0; + + RecordHeader header; + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF008: //skip OfficeArtFDG record, p. 82 [MS-ODRAW] + stream->seek(8, false); + count += 8; + break; + case 0xF003: + count += readSpgrContainer(item, header.length, stream); + break; + case 0xF004: + { + FSPContainer fspContainer; + count += readSpContainter(fspContainer, header.length, stream); + item.FSPs.push_back(fspContainer); + } + break; + default: + count += skipRecord(header, stream); + break; + } + } + return count; +} + +unsigned int DocFloatImageReader::readSpgrContainer(OfficeArtContent &item, unsigned int length, shared_ptr stream) { + //OfficeArtSpgrContainer structure is described at p.56 [MS-ODRAW] + unsigned count = 0; + RecordHeader header; + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF003: + count += readSpgrContainer(item, header.length, stream); + break; + case 0xF004: + { + FSPContainer fspContainer; + count += readSpContainter(fspContainer, header.length, stream); + item.FSPs.push_back(fspContainer); + } + break; + default: + count += skipRecord(header, stream); + break; + } + } + return count; +} + +unsigned int DocFloatImageReader::readSpContainter(FSPContainer &item, unsigned int length, shared_ptr stream) { + //OfficeArtSpContainter structure is described at p.53-55 [MS-ODRAW] + RecordHeader header; + unsigned int count = 0; + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF009: //skip OfficeArtFSPGR record, p.74 [MS-ODRAW] + stream->seek(16, false); + count += 16; + break; + case 0xF00A: + count += readFSP(item.fsp, stream); + break; + case 0xF00B: + count += readArrayFOPTE(item.fopte, header.length, stream); + break; + case 0xF00E: //OfficeArtAnchor + case 0xF00F: //OfficeArtChildAnchor, p.75 [MS-ODRAW] + case 0xF010: //OfficeArtClientAnchor + stream->seek(4, false); + count += 4; + break; + case 0xF00C: + case 0xF11F: + case 0xF11D: + break; + default: + count += skipRecord(header, stream); + break; + } + } + return count; +} + +unsigned int DocFloatImageReader::readFSP(FSP &fsp, shared_ptr stream) { + //OfficeArtFSP structure is described at p.76 [MS-ODRAW] + fsp.shapeId = read4Bytes(stream); + stream->seek(4, false); + return 8; +} + +unsigned int DocFloatImageReader::readArrayFOPTE(std::vector &fopteArray,unsigned int length, shared_ptr stream) { + //OfficeArtRGFOPTE structure is described at p.98 [MS-ODRAW] + unsigned int count = 0; + while (count < length) { + FOPTE fopte; + count += readFOPTE(fopte, stream); + fopteArray.push_back(fopte); + } + for (std::size_t i = 0; i < fopteArray.size(); ++i) { + if (fopteArray.at(i).isComplex) { + stream->seek(fopteArray.at(i).value, false); + count += fopteArray.at(i).value; + } + } + return count; +} + +unsigned int DocFloatImageReader::readFOPTE(FOPTE &fopte, shared_ptr stream) { + //OfficeArtFOPTE structure is described at p.32 [MS-ODRAW] + unsigned int dtemp; + dtemp = read2Bytes(stream); + fopte.pId = (dtemp & 0x3fff); + fopte.isBlipId = ((dtemp & 0x4000) >> 14) == 0x1; + fopte.isComplex = ((dtemp & 0x8000) >> 15) == 0x1; + fopte.value = read4Bytes(stream); + return 6; +} + +unsigned int DocFloatImageReader::read1Byte(shared_ptr stream) { + char b[1]; + if (stream->read(b, 1) != 1) { + return 0; + } + return OleUtil::getU1Byte(b, 0); +} + +unsigned int DocFloatImageReader::read2Bytes(shared_ptr stream) { + char b[2]; + if (stream->read(b, 2) != 2) { + return 0; + } + return OleUtil::getU2Bytes(b, 0); +} + +unsigned int DocFloatImageReader::read4Bytes(shared_ptr stream) { + char b[4]; + if (stream->read(b, 4) != 4) { + return 0; + } + return OleUtil::getU4Bytes(b, 0); +} diff --git a/reader/src/formats/doc/DocFloatImageReader.h b/reader/src/formats/doc/DocFloatImageReader.h new file mode 100644 index 0000000..d2d6c2e --- /dev/null +++ b/reader/src/formats/doc/DocFloatImageReader.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __DOCFLOATIMAGEREADER_H__ +#define __DOCFLOATIMAGEREADER_H__ + +#include + +class DocFloatImageReader { + +public: + struct BlipStoreEntry { // see p.68 [MS-ODRAW] + unsigned int size; // size of blip in stream + unsigned int referenceCount; // (cRef) reference count for the the blip + unsigned int offsetInDelay; // foDelay, file offset in the delay stream + }; + + struct Blip { //see p.59, p63-66 [MS-ODRAW] + BlipStoreEntry storeEntry; + unsigned int type; + ZLFileImage::Blocks blocks; + }; + + struct FSP { //see p.76-77 [MS-ODRAW] + unsigned int shapeId; //spid + }; + + struct FOPTE { //see p.98 and p.32 [MS-ODRAW] + unsigned int pId; //pid + bool isBlipId; //fBid + bool isComplex; //fComplex + unsigned int value; //op + }; + + struct FSPContainer { //see p.53-55 [MS-ODRAW] + FSP fsp; + std::vector fopte; + }; + + struct OfficeArtContent { //see p.405-406 [MS-DOC] + std::vector blips; //retrieved from OfficeArtDggContainer + std::vector FSPs; //retrieved from OfficeArtDgContainer + }; + + struct RecordHeader { //see p.26 [MS-ODRAW] + unsigned int version; + unsigned int instance; + unsigned int type; + unsigned int length; + }; + +public: + DocFloatImageReader(unsigned int off, unsigned int len, shared_ptr tableStream, shared_ptr mainStream); + +public: + void readAll(); + + ZLFileImage::Blocks getBlocksForShapeId(unsigned int shapeId) const; + +private: + static unsigned int readRecordHeader(RecordHeader &header, shared_ptr stream); + static unsigned int readDggContainer(OfficeArtContent &item, unsigned int length, shared_ptr stream, shared_ptr mainStream); + + static unsigned int readBStoreContainer(OfficeArtContent &item, unsigned int length, shared_ptr stream, shared_ptr mainStream); + static unsigned int readBStoreContainerFileBlock(Blip &blip, shared_ptr stream, shared_ptr mainStream); + static unsigned int readBlip(Blip &blip, const RecordHeader &header, shared_ptr stream); + static unsigned int readFBSE(BlipStoreEntry &fbse, shared_ptr stream); + + static unsigned int readFOPTE(FOPTE &fopte, shared_ptr stream); + static unsigned int readArrayFOPTE(std::vector &fopte, unsigned int length, shared_ptr stream); + static unsigned int readFSP(FSP &fsp, shared_ptr stream); + static unsigned int readSpContainter(FSPContainer &item, unsigned int length, shared_ptr stream); + static unsigned int readSpgrContainer(OfficeArtContent &item, unsigned int length, shared_ptr stream); + static unsigned int readDgContainer(OfficeArtContent &item, unsigned int length, shared_ptr stream); + + static unsigned int skipRecord(const RecordHeader &header, shared_ptr stream); + + static unsigned int read1Byte(shared_ptr stream); + static unsigned int read2Bytes(shared_ptr stream); + static unsigned int read4Bytes(shared_ptr stream); + +private: + shared_ptr myTableStream; + shared_ptr myMainStream; + unsigned int myOffset; + unsigned int myLength; + + OfficeArtContent myItem; +}; + +#endif /* __DOCFLOATIMAGEREADER_H__ */ diff --git a/reader/src/formats/doc/DocInlineImageReader.cpp b/reader/src/formats/doc/DocInlineImageReader.cpp new file mode 100644 index 0000000..69ce74f --- /dev/null +++ b/reader/src/formats/doc/DocInlineImageReader.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2004-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 "OleUtil.h" +#include "OleMainStream.h" + +#include "DocInlineImageReader.h" + +DocInlineImageReader::DocInlineImageReader(shared_ptr dataStream) : + myDataStream(dataStream) { +} + +ZLFileImage::Blocks DocInlineImageReader::getImagePieceInfo(unsigned int dataPos) { + if (myDataStream.isNull()) { + return ZLFileImage::Blocks(); + } + if (!myDataStream->seek(dataPos, true)) { + return ZLFileImage::Blocks(); + } + + //reading PICF structure (see p. 421 [MS-DOC]) + unsigned int picfHeaderSize = 4 + 2 + 8; //record length, headerLength and storage format + char headerBuffer[picfHeaderSize]; + if (myDataStream->read(headerBuffer, picfHeaderSize) != picfHeaderSize) { + return ZLFileImage::Blocks(); + } + unsigned int length = OleUtil::getU4Bytes(headerBuffer, 0); + unsigned int headerLength = OleUtil::getU2Bytes(headerBuffer, 4); + unsigned int formatType = OleUtil::getU2Bytes(headerBuffer, 6); + + if (formatType != 0x0064) { //external link to some file; see p.394 [MS-DOC] + //TODO implement + return ZLFileImage::Blocks(); + } + if (headerLength >= length) { + return ZLFileImage::Blocks(); + } + + //reading OfficeArtInlineSpContainer structure; see p.421 [MS-DOC] and p.56 [MS-ODRAW] + if (!myDataStream->seek(headerLength - picfHeaderSize, false)) { //skip header + return ZLFileImage::Blocks(); + } + + char buffer[8]; //for OfficeArtRecordHeader structure; see p.69 [MS-ODRAW] + bool found = false; + unsigned int curOffset = 0; + for (curOffset = headerLength; !found && curOffset + 8 <= length; curOffset += 8) { + if (myDataStream->read(buffer, 8) != 8) { + return ZLFileImage::Blocks(); + } + unsigned int recordInstance = OleUtil::getU2Bytes(buffer, 0) >> 4; + unsigned int recordType = OleUtil::getU2Bytes(buffer, 2); + unsigned int recordLen = OleUtil::getU4Bytes(buffer, 4); + + switch (recordType) { + case 0xF000: case 0xF001: case 0xF002: case 0xF003: case 0xF004: case 0xF005: + break; + case 0xF007: + { + myDataStream->seek(33, false); + char tmpBuf[1]; + myDataStream->read(tmpBuf, 1); + unsigned int nameLength = OleUtil::getU1Byte(tmpBuf, 0); + myDataStream->seek(nameLength * 2 + 2, false); + curOffset += 33 + 1 + nameLength * 2 + 2; + } + break; + case 0xF008: + myDataStream->seek(8, false); + curOffset += 8; + break; + case 0xF009: + myDataStream->seek(16, false); + curOffset += 16; + break; + case 0xF006: case 0xF00A: case 0xF00B: case 0xF00D: case 0xF00E: case 0xF00F: case 0xF010: case 0xF011: case 0xF122: + myDataStream->seek(recordLen, false); + curOffset += recordLen; + break; + case OleMainStream::IMAGE_EMF: + case OleMainStream::IMAGE_WMF: + case OleMainStream::IMAGE_PICT: + //TODO implement + return ZLFileImage::Blocks(); + case OleMainStream::IMAGE_JPEG: + case OleMainStream::IMAGE_JPEG2: + myDataStream->seek(17, false); + curOffset += 17; + if (recordInstance == 0x46B || recordInstance == 0x6E3) { + myDataStream->seek(16, false); + curOffset += 16; + } + found = true; + break; + case OleMainStream::IMAGE_PNG: + myDataStream->seek(17, false); + curOffset += 17; + if (recordInstance == 0x6E1) { + myDataStream->seek(16, false); + curOffset += 16; + } + found = true; + break; + case OleMainStream::IMAGE_DIB: // DIB = BMP without 14-bytes header + myDataStream->seek(17, false); + curOffset += 17; + if (recordInstance == 0x7A9) { + myDataStream->seek(16, false); + curOffset += 16; + } + found = true; + break; + case OleMainStream::IMAGE_TIFF: + myDataStream->seek(17, false); + curOffset += 17; + if (recordInstance == 0x6E5) { + myDataStream->seek(16, false); + curOffset += 16; + } + found = true; + break; + case 0xF00C: + default: + return ZLFileImage::Blocks(); + } + } + + if (!found) { + return ZLFileImage::Blocks(); + } + return myDataStream->getBlockPieceInfoList(dataPos + curOffset, length - curOffset); +} diff --git a/reader/src/formats/doc/DocInlineImageReader.h b/reader/src/formats/doc/DocInlineImageReader.h new file mode 100644 index 0000000..9dab9ae --- /dev/null +++ b/reader/src/formats/doc/DocInlineImageReader.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __DOCINLINEIMAGEREADER_H__ +#define __DOCINLINEIMAGEREADER_H__ + +#include + +#include "OleStream.h" + +class DocInlineImageReader { + +public: + DocInlineImageReader(shared_ptr dataStream); + ZLFileImage::Blocks getImagePieceInfo(unsigned int dataPos); + +private: + shared_ptr myDataStream; +}; + +#endif /* __DOCINLINEIMAGEREADER_H__ */ diff --git a/reader/src/formats/doc/DocMetaInfoReader.cpp b/reader/src/formats/doc/DocMetaInfoReader.cpp new file mode 100644 index 0000000..37b39c2 --- /dev/null +++ b/reader/src/formats/doc/DocMetaInfoReader.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2004-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 "../../library/Book.h" + +#include "DocMetaInfoReader.h" + +DocMetaInfoReader::DocMetaInfoReader(Book &book) : myBook(book) { + myBook.removeAllAuthors(); + myBook.setTitle(std::string()); + myBook.setLanguage(std::string()); + myBook.removeAllTags(); +} + +bool DocMetaInfoReader::readMetaInfo() { + myBook.removeAllAuthors(); + myBook.setTitle(myBook.file().name(true)); + myBook.removeAllTags(); + return true; +} diff --git a/reader/src/formats/doc/DocMetaInfoReader.h b/reader/src/formats/doc/DocMetaInfoReader.h new file mode 100644 index 0000000..db26d29 --- /dev/null +++ b/reader/src/formats/doc/DocMetaInfoReader.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __DOCMETAINFOREADER_H__ +#define __DOCMETAINFOREADER_H__ + +#include + +class Book; + +class DocMetaInfoReader { + +public: + DocMetaInfoReader(Book &book); + ~DocMetaInfoReader(); + bool readMetaInfo(); + + /* + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + */ + +private: + Book &myBook; +}; + +inline DocMetaInfoReader::~DocMetaInfoReader() {} + +#endif /* __DOCMETAINFOREADER_H__ */ diff --git a/reader/src/formats/doc/DocPlugin.cpp b/reader/src/formats/doc/DocPlugin.cpp new file mode 100644 index 0000000..ef6f511 --- /dev/null +++ b/reader/src/formats/doc/DocPlugin.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2004-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 + +#include "DocPlugin.h" +#include "DocMetaInfoReader.h" +#include "DocBookReader.h" +#include "DocStreams.h" +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +DocPlugin::DocPlugin() { +} + +DocPlugin::~DocPlugin() { +} + +bool DocPlugin::providesMetaInfo() const { + return true; +} + +const std::string DocPlugin::supportedFileType() const { + return "doc"; +} + +bool DocPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "doc"; +} + +bool DocPlugin::readMetaInfo(Book &book) const { + if (!DocMetaInfoReader(book).readMetaInfo()) { + return false; + } + + shared_ptr stream = new DocAnsiStream(book.file(), 50000); + if (!detectEncodingAndLanguage(book, *stream)) { + stream = new DocUcs2Stream(book.file(), 50000); + detectLanguage(book, *stream, ZLEncodingConverter::UTF8, true); + } + + return true; +} + +bool DocPlugin::readLanguageAndEncoding(Book &/*book*/) const { + return true; +} + +bool DocPlugin::readModel(BookModel &model) const { + return DocBookReader(model, model.book()->encoding()).readBook(); +} diff --git a/reader/src/formats/doc/DocPlugin.h b/reader/src/formats/doc/DocPlugin.h new file mode 100644 index 0000000..93b1803 --- /dev/null +++ b/reader/src/formats/doc/DocPlugin.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __DOCPLUGIN_H__ +#define __DOCPLUGIN_H__ + +#include "../FormatPlugin.h" + +class DocPlugin : public FormatPlugin { + +public: + DocPlugin(); + ~DocPlugin(); + bool providesMetaInfo() const; + + const std::string supportedFileType() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; +}; + +#endif /* __DOCPLUGIN_H__ */ diff --git a/reader/src/formats/doc/DocStreams.cpp b/reader/src/formats/doc/DocStreams.cpp new file mode 100644 index 0000000..b21e15a --- /dev/null +++ b/reader/src/formats/doc/DocStreams.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2004-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 "DocStreams.h" +#include "OleStreamReader.h" + +class DocReader : public OleStreamReader { + +public: + DocReader(char *buffer, std::size_t maxSize); + ~DocReader(); + std::size_t readSize() const; + +private: + bool readStream(OleMainStream &stream); + void ansiDataHandler(const char *buffer, std::size_t len); + void ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol); + void footnotesStartHandler(); + +protected: + char *myBuffer; + const std::size_t myMaxSize; + std::size_t myActualSize; +}; + +class DocAnsiReader : public DocReader { + +public: + DocAnsiReader(char *buffer, std::size_t maxSize); + ~DocAnsiReader(); + +private: + void ansiDataHandler(const char *buffer, std::size_t len); +}; + +class DocUcs2Reader : public DocReader { + +public: + DocUcs2Reader(char *buffer, std::size_t maxSize); + ~DocUcs2Reader(); + +private: + void ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol); +}; + +DocReader::DocReader(char *buffer, std::size_t maxSize) : myBuffer(buffer), myMaxSize(maxSize), myActualSize(0) { +} + +DocReader::~DocReader() { +} + +bool DocReader::readStream(OleMainStream &stream) { + // TODO make 2 optmizations: + // 1) If another piece is too big, reading of next piece can be stopped if some size parameter will be specified + // (it can be transfered as a parameter (with default 0 value, that means no need to use it) to readNextPiece method) + // 2) We can specify as a parameter for readNextPiece, what kind of piece should be read next (ANSI or not ANSI). + // As type of piece is known already, there's no necessary to read other pieces. + while (myActualSize < myMaxSize) { + if (!readNextPiece(stream)) { + break; + } + } + return true; +} + +void DocReader::ansiDataHandler(const char*, std::size_t) { +} + +void DocReader::ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char) { +} + +void DocReader::footnotesStartHandler() { +} + +std::size_t DocReader::readSize() const { + return myActualSize; +} + +DocAnsiReader::DocAnsiReader(char *buffer, std::size_t maxSize) : DocReader(buffer, maxSize) { +} + +DocAnsiReader::~DocAnsiReader() { +} + +void DocAnsiReader::ansiDataHandler(const char *buffer, std::size_t dataLength) { + if (myActualSize < myMaxSize) { + const std::size_t len = std::min(dataLength, myMaxSize - myActualSize); + std::strncpy(myBuffer + myActualSize, buffer, len); + myActualSize += len; + } +} + +DocUcs2Reader::DocUcs2Reader(char *buffer, std::size_t maxSize) : DocReader(buffer, maxSize) { +} + +DocUcs2Reader::~DocUcs2Reader() { +} + +void DocUcs2Reader::ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol) { + if (myActualSize < myMaxSize) { + char buffer[4]; + const std::size_t dataLength = ZLUnicodeUtil::ucs2ToUtf8(buffer, symbol); + const std::size_t len = std::min(dataLength, myMaxSize - myActualSize); + std::strncpy(myBuffer + myActualSize, buffer, len); + myActualSize += len; + } +} + +DocStream::DocStream(const ZLFile& file, std::size_t maxSize) : myFile(file), myBuffer(0), mySize(maxSize) { +} + +DocStream::~DocStream() { + close(); +} + +bool DocStream::open() { + if (mySize != 0) { + myBuffer = new char[mySize]; + } + shared_ptr reader = createReader(myBuffer, mySize); + shared_ptr stream = myFile.inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + if (!reader->readDocument(stream, false)) { + return false; + } + mySize = reader->readSize(); + myOffset = 0; + return true; +} + +std::size_t DocStream::read(char *buffer, std::size_t maxSize) { + maxSize = std::min(maxSize, mySize - myOffset); + if (buffer != 0 && myBuffer != 0) { + std::memcpy(buffer, myBuffer + myOffset, maxSize); + } + myOffset += maxSize; + return maxSize; +} + +void DocStream::close() { + if (myBuffer != 0) { + delete[] myBuffer; + myBuffer = 0; + } +} + +void DocStream::seek(int offset, bool absoluteOffset) { + if (!absoluteOffset) { + offset += myOffset; + } + myOffset = std::min(mySize, (std::size_t)std::max(0, offset)); +} + +std::size_t DocStream::offset() const { + return myOffset; +} + +std::size_t DocStream::sizeOfOpened() { + return mySize; +} + +DocAnsiStream::DocAnsiStream(const ZLFile& file, std::size_t maxSize) : DocStream(file, maxSize) { +} + +DocAnsiStream::~DocAnsiStream() { +} + +shared_ptr DocAnsiStream::createReader(char *buffer, std::size_t maxSize) { + return new DocAnsiReader(buffer, maxSize); +} + +DocUcs2Stream::DocUcs2Stream(const ZLFile& file, std::size_t maxSize) : DocStream(file, maxSize) { +} + +DocUcs2Stream::~DocUcs2Stream() { +} + +shared_ptr DocUcs2Stream::createReader(char *buffer, std::size_t maxSize) { + return new DocUcs2Reader(buffer, maxSize); +} diff --git a/reader/src/formats/doc/DocStreams.h b/reader/src/formats/doc/DocStreams.h new file mode 100644 index 0000000..4b1538a --- /dev/null +++ b/reader/src/formats/doc/DocStreams.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __DOCSTREAMS_H__ +#define __DOCSTREAMS_H__ + +#include +#include + +class DocReader; + +class DocStream : public ZLInputStream { + +public: + DocStream(const ZLFile& file, std::size_t maxSize); + ~DocStream(); + +private: + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +protected: + virtual shared_ptr createReader(char *buffer, std::size_t maxSize) = 0; + +private: + const ZLFile myFile; + char *myBuffer; + std::size_t mySize; + std::size_t myOffset; +}; + +class DocAnsiStream : public DocStream { + +public: + DocAnsiStream(const ZLFile& file, std::size_t maxSize); + ~DocAnsiStream(); + +private: + shared_ptr createReader(char *buffer, std::size_t maxSize); +}; + +class DocUcs2Stream : public DocStream { + +public: + DocUcs2Stream(const ZLFile& file, std::size_t maxSize); + ~DocUcs2Stream(); + +private: + shared_ptr createReader(char *buffer, std::size_t maxSize); +}; + +#endif /* __DOCSTREAMS_H__ */ diff --git a/reader/src/formats/doc/OleMainStream.cpp b/reader/src/formats/doc/OleMainStream.cpp new file mode 100644 index 0000000..fe829e6 --- /dev/null +++ b/reader/src/formats/doc/OleMainStream.cpp @@ -0,0 +1,1085 @@ +/* + * Copyright (C) 2004-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 "OleUtil.h" +#include "OleStorage.h" + +#include "DocInlineImageReader.h" + +#include "OleMainStream.h" + +OleMainStream::Style::Style() : + StyleIdCurrent(STYLE_INVALID), + StyleIdNext(STYLE_INVALID), + HasPageBreakBefore(false), + BeforeParagraphIndent(0), + AfterParagraphIndent(0), + LeftIndent(0), + FirstLineIndent(0), + RightIndent(0), + Alignment(ALIGNMENT_DEFAULT) { +} + +OleMainStream::CharInfo::CharInfo() : FontStyle(FONT_REGULAR), FontSize(20) { +} + +OleMainStream::SectionInfo::SectionInfo() : CharPosition(0), IsNewPage(true) { +} + +OleMainStream::InlineImageInfo::InlineImageInfo() : DataPosition(0) { +} + +OleMainStream::FloatImageInfo::FloatImageInfo() : ShapeId(0) { +} + +OleMainStream::OleMainStream(shared_ptr storage, OleEntry oleEntry, shared_ptr stream) : OleStream(storage, oleEntry, stream) { +} + +bool OleMainStream::open(bool doReadFormattingData) { + if (OleStream::open() == false) { + return false; + } + + static const std::size_t HEADER_SIZE = 768; //size of data in header of main stream + char headerBuffer[HEADER_SIZE]; + seek(0, true); + + if (read(headerBuffer, HEADER_SIZE) != HEADER_SIZE) { + return false; + } + + bool result = readFIB(headerBuffer); + if (!result) { + return false; + } + + // determining table stream number + unsigned int tableNumber = (OleUtil::getU2Bytes(headerBuffer, 0xA) & 0x0200) ? 1 : 0; + std::string tableName = tableNumber == 0 ? "0" : "1"; + tableName += "Table"; + OleEntry tableEntry; + result = myStorage->getEntryByName(tableName, tableEntry); + + if (!result) { + // cant't find table stream (that can be only in case if file format is below Word 7/8), so building simple table stream + // TODO: CHECK may be not all old documents have ANSI + ZLLogger::Instance().println("DocPlugin", "cant't find table stream, building own simple piece table, that includes all charachters"); + Piece piece = {myStartOfText, myEndOfText - myStartOfText, true, Piece::PIECE_TEXT, 0}; + myPieces.push_back(piece); + return true; + } + + result = readPieceTable(headerBuffer, tableEntry); + + if (!result) { + ZLLogger::Instance().println("DocPlugin", "error during reading piece table"); + return false; + } + + if (!doReadFormattingData) { + return true; + } + + OleEntry dataEntry; + if (myStorage->getEntryByName("Data", dataEntry)) { + myDataStream = new OleStream(myStorage, dataEntry, myBaseStream); + } + + //result of reading following structures doesn't check, because all these + //problems can be ignored, and document can be showed anyway, maybe with wrong formatting + readBookmarks(headerBuffer, tableEntry); + readStylesheet(headerBuffer, tableEntry); + //readSectionsInfoTable(headerBuffer, tableEntry); //it isn't used now + readParagraphStyleTable(headerBuffer, tableEntry); + readCharInfoTable(headerBuffer, tableEntry); + readFloatingImages(headerBuffer, tableEntry); + return true; +} + +const OleMainStream::Pieces &OleMainStream::getPieces() const { + return myPieces; +} + +const OleMainStream::CharInfoList &OleMainStream::getCharInfoList() const { + return myCharInfoList; +} + +const OleMainStream::StyleInfoList &OleMainStream::getStyleInfoList() const { + return myStyleInfoList; +} + +const OleMainStream::BookmarksList &OleMainStream::getBookmarks() const { + return myBookmarks; +} + +const OleMainStream::InlineImageInfoList &OleMainStream::getInlineImageInfoList() const { + return myInlineImageInfoList; +} + +const OleMainStream::FloatImageInfoList &OleMainStream::getFloatImageInfoList() const { + return myFloatImageInfoList; +} + +ZLFileImage::Blocks OleMainStream::getFloatImage(unsigned int shapeId) const { + if (myFLoatImageReader.isNull()) { + return ZLFileImage::Blocks(); + } + return myFLoatImageReader->getBlocksForShapeId(shapeId); +} + +ZLFileImage::Blocks OleMainStream::getInlineImage(unsigned int dataPosition) const { + if (myDataStream.isNull()) { + return ZLFileImage::Blocks(); + } + DocInlineImageReader imageReader(myDataStream); + return imageReader.getImagePieceInfo(dataPosition); +} + +bool OleMainStream::readFIB(const char *headerBuffer) { + int flags = OleUtil::getU2Bytes(headerBuffer, 0xA); //offset for flags + + if (flags & 0x0004) { //flag for complex format + ZLLogger::Instance().println("DocPlugin", "This was fast-saved. Some information is lost"); + //lostInfo = (flags & 0xF0) >> 4); + } + + if (flags & 0x1000) { //flag for using extending charset + ZLLogger::Instance().println("DocPlugin", "File uses extended character set (get_word8_char)"); + } else { + ZLLogger::Instance().println("DocPlugin", "File uses get_8bit_char character set"); + } + + if (flags & 0x100) { //flag for encrypted files + ZLLogger::Instance().println("DocPlugin", "File is encrypted"); + // Encryption key = %08lx ; NumUtil::get4Bytes(header, 14) + return false; + } + + unsigned int charset = OleUtil::getU2Bytes(headerBuffer, 0x14); //offset for charset number + if (charset && charset != 0x100) { //0x100 = default charset + ZLLogger::Instance().println("DocPlugin", "Using not default character set %d"); + } else { + ZLLogger::Instance().println("DocPlugin", "Using default character set"); + } + + myStartOfText = OleUtil::get4Bytes(headerBuffer, 0x18); //offset for start of text value + myEndOfText = OleUtil::get4Bytes(headerBuffer, 0x1c); //offset for end of text value + return true; +} + +void OleMainStream::splitPieces(const Pieces &s, Pieces &dest1, Pieces &dest2, Piece::PieceType type1, Piece::PieceType type2, int boundary) { + Pieces source = s; + dest1.clear(); + dest2.clear(); + + int sumLength = 0; + std::size_t i = 0; + for (i = 0; i < source.size(); ++i) { + Piece piece = source.at(i); + if (piece.Length + sumLength >= boundary) { + Piece piece2 = piece; + + piece.Length = boundary - sumLength; + piece.Type = type1; + + piece2.Type = type2; + piece2.Offset += piece.Length * 2; + piece2.Length -= piece.Length; + + if (piece.Length > 0) { + dest1.push_back(piece); + } + if (piece2.Length > 0) { + dest2.push_back(piece2); + } + ++i; + break; + } + sumLength += piece.Length; + piece.Type = type1; + dest1.push_back(piece); + } + for (; i < source.size(); ++i) { + Piece piece = source.at(i); + piece.Type = type2; + dest2.push_back(piece); + } + +} + +std::string OleMainStream::getPiecesTableBuffer(const char *headerBuffer, OleStream &tableStream) { + unsigned int clxOffset = OleUtil::getU4Bytes(headerBuffer, 0x01A2); //offset for CLX structure + unsigned int clxLength = OleUtil::getU4Bytes(headerBuffer, 0x01A6); //offset for value of CLX structure length + + //1 step : loading CLX table from table stream + char *clxBuffer = new char[clxLength]; + if (!tableStream.seek(clxOffset, true)) { + ZLLogger::Instance().println("DocPlugin", "getPiecesTableBuffer -- error for seeking to CLX structure"); + return std::string(); + } + if (tableStream.read(clxBuffer, clxLength) != clxLength) { + ZLLogger::Instance().println("DocPlugin", "getPiecesTableBuffer -- CLX structure length is invalid"); + return std::string(); + } + std::string clx(clxBuffer, clxLength); + delete[] clxBuffer; + + //2 step: searching for pieces table buffer at CLX + //(determines it by 0x02 as start symbol) + std::size_t from = 0; + std::size_t i; + std::string pieceTableBuffer; + while ((i = clx.find_first_of(0x02, from)) != std::string::npos) { + if (clx.size() < i + 1 + 4) { + ZLLogger::Instance().println("DocPlugin", "getPiecesTableBuffer -- CLX structure has invalid format"); + return std::string(); + } + unsigned int pieceTableLength = OleUtil::getU4Bytes(clx.c_str(), i + 1); + pieceTableBuffer = std::string(clx, i + 1 + 4); + if (pieceTableBuffer.length() != pieceTableLength) { + from = i + 1; + continue; + } + break; + } + return pieceTableBuffer; +} + + +bool OleMainStream::readPieceTable(const char *headerBuffer, const OleEntry &tableEntry) { + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string piecesTableBuffer = getPiecesTableBuffer(headerBuffer, tableStream); + + if (piecesTableBuffer.empty()) { + return false; + } + + //getting count of Character Positions for different types of subdocuments in Main Stream + int ccpText = OleUtil::get4Bytes(headerBuffer, 0x004C); //text + int ccpFtn = OleUtil::get4Bytes(headerBuffer, 0x0050); //footnote subdocument + int ccpHdd = OleUtil::get4Bytes(headerBuffer, 0x0054); //header subdocument + int ccpMcr = OleUtil::get4Bytes(headerBuffer, 0x0058); //macro subdocument + int ccpAtn = OleUtil::get4Bytes(headerBuffer, 0x005C); //comment subdocument + int ccpEdn = OleUtil::get4Bytes(headerBuffer, 0x0060); //endnote subdocument + int ccpTxbx = OleUtil::get4Bytes(headerBuffer, 0x0064); //textbox subdocument + int ccpHdrTxbx = OleUtil::get4Bytes(headerBuffer, 0x0068); //textbox subdocument of the header + int lastCP = ccpFtn + ccpHdd + ccpMcr + ccpAtn + ccpEdn + ccpTxbx + ccpHdrTxbx; + if (lastCP != 0) { + ++lastCP; + } + lastCP += ccpText; + + //getting the CP (character positions) and CP descriptors + std::vector cp; //array of character positions for pieces + unsigned int j = 0; + for (j = 0; ; j += 4) { + if (piecesTableBuffer.size() < j + 4) { + ZLLogger::Instance().println("DocPlugin", "invalid piece table, cp ends not with a lastcp"); + break; + } + int curCP = OleUtil::get4Bytes(piecesTableBuffer.c_str(), j); + cp.push_back(curCP); + if (curCP == lastCP) { + break; + } + } + + if (cp.size() < 2) { + ZLLogger::Instance().println("DocPlugin", "invalid piece table, < 2 pieces"); + return false; + } + + std::vector descriptors; + for (std::size_t k = 0; k < cp.size() - 1; ++k) { + //j + 4, because it should be taken after CP in PiecesTable Buffer + //k * 8, because it should be taken 8 byte for each descriptor + std::size_t substrFrom = j + 4 + k * 8; + if (piecesTableBuffer.size() < substrFrom + 8) { + ZLLogger::Instance().println("DocPlugin", "invalid piece table, problems with descriptors reading"); + break; + } + descriptors.push_back(piecesTableBuffer.substr(substrFrom, 8)); + } + + //filling the Pieces vector + std::size_t minValidSize = std::min(cp.size() - 1, descriptors.size()); + if (minValidSize == 0) { + ZLLogger::Instance().println("DocPlugin", "invalid piece table, there are no pieces"); + return false; + } + + for (std::size_t i = 0; i < minValidSize; ++i) { + //4byte integer with offset and ANSI flag + int fcValue = OleUtil::get4Bytes(descriptors.at(i).c_str(), 0x2); //offset for piece structure + Piece piece; + piece.IsANSI = (fcValue & 0x40000000) == 0x40000000; //ansi flag + piece.Offset = fcValue & 0x3FFFFFFF; //gettting offset for current piece + piece.Length = cp.at(i + 1) - cp.at(i); + myPieces.push_back(piece); + } + + //split pieces into different types + Pieces piecesText, piecesFootnote, piecesOther; + splitPieces(myPieces, piecesText, piecesFootnote, Piece::PIECE_TEXT, Piece::PIECE_FOOTNOTE, ccpText); + splitPieces(piecesFootnote, piecesFootnote, piecesOther, Piece::PIECE_FOOTNOTE, Piece::PIECE_OTHER, ccpFtn); + + myPieces.clear(); + for (std::size_t i = 0; i < piecesText.size(); ++i) { + myPieces.push_back(piecesText.at(i)); + } + for (std::size_t i = 0; i < piecesFootnote.size(); ++i) { + myPieces.push_back(piecesFootnote.at(i)); + } + for (std::size_t i = 0; i < piecesOther.size(); ++i) { + myPieces.push_back(piecesOther.at(i)); + } + + //converting length and offset depending on isANSI + for (std::size_t i = 0; i < myPieces.size(); ++i) { + Piece &piece = myPieces.at(i); + if (!piece.IsANSI) { + piece.Length *= 2; + } else { + piece.Offset /= 2; + } + } + + //filling startCP field + unsigned int curStartCP = 0; + for (std::size_t i = 0; i < myPieces.size(); ++i) { + Piece &piece = myPieces.at(i); + piece.startCP = curStartCP; + if (piece.IsANSI) { + curStartCP += piece.Length; + } else { + curStartCP += piece.Length / 2; + } + } + return true; +} + +bool OleMainStream::readBookmarks(const char *headerBuffer, const OleEntry &tableEntry) { + //SttbfBkmk structure is a table of bookmark name strings + unsigned int beginNamesInfo = OleUtil::getU4Bytes(headerBuffer, 0x142); // address of SttbfBkmk structure + std::size_t namesInfoLength = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0x146); // length of SttbfBkmk structure + + if (namesInfoLength == 0) { + return true; //there's no bookmarks + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginNamesInfo, namesInfoLength, tableStream)) { + return false; + } + + unsigned int recordsNumber = OleUtil::getU2Bytes(buffer.c_str(), 0x2); //count of records + + std::vector names; + unsigned int offset = 0x6; //initial offset + for (unsigned int i = 0; i < recordsNumber; ++i) { + if (buffer.size() < offset + 2) { + ZLLogger::Instance().println("DocPlugin", "problmes with reading bookmarks names"); + break; + } + unsigned int length = OleUtil::getU2Bytes(buffer.c_str(), offset) * 2; //length of string in bytes + ZLUnicodeUtil::Ucs2String name; + for (unsigned int j = 0; j < length; j+=2) { + char ch1 = buffer.at(offset + 2 + j); + char ch2 = buffer.at(offset + 2 + j + 1); + ZLUnicodeUtil::Ucs2Char ucs2Char = (unsigned int)ch1 | ((unsigned int)ch2 << 8); + name.push_back(ucs2Char); + } + std::string utf8Name; + ZLUnicodeUtil::ucs2ToUtf8(utf8Name, name); + names.push_back(utf8Name); + offset += length + 2; + } + + //plcfBkmkf structure is table recording beginning CPs of bookmarks + unsigned int beginCharPosInfo = OleUtil::getU4Bytes(headerBuffer, 0x14A); // address of plcfBkmkf structure + std::size_t charPosInfoLen = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0x14E); // length of plcfBkmkf structure + + if (charPosInfoLen == 0) { + return true; //there's no bookmarks + } + + if (!readToBuffer(buffer, beginCharPosInfo, charPosInfoLen, tableStream)) { + return false; + } + + static const unsigned int BKF_SIZE = 4; + std::size_t size = calcCountOfPLC(charPosInfoLen, BKF_SIZE); + std::vector charPage; + for (std::size_t index = 0, offset = 0; index < size; ++index, offset += 4) { + charPage.push_back(OleUtil::getU4Bytes(buffer.c_str(), offset)); + } + + for (std::size_t i = 0; i < names.size(); ++i) { + if (i >= charPage.size()) { + break; //for the case if something in these structures goes wrong, to not to lose all bookmarks + } + Bookmark bookmark; + bookmark.CharPosition = charPage.at(i); + bookmark.Name = names.at(i); + myBookmarks.push_back(bookmark); + } + + return true; +} + +bool OleMainStream::readStylesheet(const char *headerBuffer, const OleEntry &tableEntry) { + //STSH structure is a stylesheet + unsigned int beginStshInfo = OleUtil::getU4Bytes(headerBuffer, 0xa2); // address of STSH structure + std::size_t stshInfoLength = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0xa6); // length of STSH structure + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + char *buffer = new char[stshInfoLength]; + if (!tableStream.seek(beginStshInfo, true)) { + ZLLogger::Instance().println("DocPlugin", "problems with reading STSH structure"); + return false; + } + if (tableStream.read(buffer, stshInfoLength) != stshInfoLength) { + ZLLogger::Instance().println("DocPlugin", "problems with reading STSH structure, invalid length"); + return false; + } + + std::size_t stdCount = (std::size_t)OleUtil::getU2Bytes(buffer, 2); + std::size_t stdBaseInFile = (std::size_t)OleUtil::getU2Bytes(buffer, 4); + myStyleSheet.resize(stdCount); + + std::vector isFilled; + isFilled.resize(stdCount, false); + + std::size_t stdLen = 0; + bool styleSheetWasChanged = false; + do { //make it in while loop, because some base style can be after their successors + styleSheetWasChanged = false; + for (std::size_t index = 0, offset = 2 + (std::size_t)OleUtil::getU2Bytes(buffer, 0); index < stdCount; index++, offset += 2 + stdLen) { + stdLen = (std::size_t)OleUtil::getU2Bytes(buffer, offset); + if (isFilled.at(index)) { + continue; + } + + if (stdLen == 0) { + //if record is empty, left it default + isFilled[index] = true; + continue; + } + + Style styleInfo = myStyleSheet.at(index); + + const unsigned int styleAndBaseType = OleUtil::getU2Bytes(buffer, offset + 4); + const unsigned int styleType = styleAndBaseType % 16; + const unsigned int baseStyleId = styleAndBaseType / 16; + if (baseStyleId == Style::STYLE_NIL || baseStyleId == Style::STYLE_USER) { + //if based on nil or user style, left default + } else { + int baseStyleIndex = getStyleIndex(baseStyleId, isFilled, myStyleSheet); + if (baseStyleIndex < 0) { + //this base style is not filled yet, so pass it at some time + continue; + } + styleInfo = myStyleSheet.at(baseStyleIndex); + styleInfo.StyleIdCurrent = Style::STYLE_INVALID; + } + + // parse STD structure + unsigned int tmp = OleUtil::getU2Bytes(buffer, offset + 6); + unsigned int upxCount = tmp % 16; + styleInfo.StyleIdNext = tmp / 16; + + //adding current style + myStyleSheet[index] = styleInfo; + isFilled[index] = true; + styleSheetWasChanged = true; + + std::size_t pos = 2 + stdBaseInFile; + std::size_t nameLen = (std::size_t)OleUtil::getU2Bytes(buffer, offset + pos); + nameLen = nameLen * 2 + 2; //from Unicode characters to bytes + Unicode null charachter length + pos += 2 + nameLen; + if (pos % 2 != 0) { + ++pos; + } + if (pos >= stdLen) { + continue; + } + std::size_t upxLen = (std::size_t)OleUtil::getU2Bytes(buffer, offset + pos); + if (pos + upxLen > stdLen) { + //UPX length too large + continue; + } + //for style info styleType must be equal 1 + if (styleType == 1 && upxCount >= 1) { + if (upxLen >= 2) { + styleInfo.StyleIdCurrent = OleUtil::getU2Bytes(buffer, offset + pos + 2); + getStyleInfo(0, buffer + offset + pos + 4, upxLen - 2, styleInfo); + myStyleSheet[index] = styleInfo; + } + pos += 2 + upxLen; + if (pos % 2 != 0) { + ++pos; + } + upxLen = (std::size_t)OleUtil::getU2Bytes(buffer, offset + pos); + } + if (upxLen == 0 || pos + upxLen > stdLen) { + //too small/too large + continue; + } + //for char info styleType can be equal 1 or 2 + if ((styleType == 1 && upxCount >= 2) || (styleType == 2 && upxCount >= 1)) { + CharInfo charInfo; + getCharInfo(0, Style::STYLE_INVALID, buffer + offset + pos + 2, upxLen, charInfo); + styleInfo.CurrentCharInfo = charInfo; + myStyleSheet[index] = styleInfo; + } + } + } while (styleSheetWasChanged); + delete[] buffer; + return true; +} + +bool OleMainStream::readCharInfoTable(const char *headerBuffer, const OleEntry &tableEntry) { + //PlcfbteChpx structure is table with formatting for particular run of text + unsigned int beginCharInfo = OleUtil::getU4Bytes(headerBuffer, 0xfa); // address of PlcfbteChpx structure + std::size_t charInfoLength = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0xfe); // length of PlcfbteChpx structure + if (charInfoLength < 4) { + return false; + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginCharInfo, charInfoLength, tableStream)) { + return false; + } + + static const unsigned int CHPX_SIZE = 4; + std::size_t size = calcCountOfPLC(charInfoLength, CHPX_SIZE); + std::vector charBlocks; + for (std::size_t index = 0, offset = (size + 1) * 4; index < size; ++index, offset += CHPX_SIZE) { + charBlocks.push_back(OleUtil::getU4Bytes(buffer.c_str(), offset)); + } + + char *formatPageBuffer = new char[OleStorage::BBD_BLOCK_SIZE]; + for (std::size_t index = 0; index < charBlocks.size(); ++index) { + seek(charBlocks.at(index) * OleStorage::BBD_BLOCK_SIZE, true); + if (read(formatPageBuffer, OleStorage::BBD_BLOCK_SIZE) != OleStorage::BBD_BLOCK_SIZE) { + return false; + } + unsigned int crun = OleUtil::getU1Byte(formatPageBuffer, 0x1ff); //offset with crun (count of 'run of text') + for (unsigned int index2 = 0; index2 < crun; ++index2) { + unsigned int offset = OleUtil::getU4Bytes(formatPageBuffer, index2 * 4); + unsigned int chpxOffset = 2 * OleUtil::getU1Byte(formatPageBuffer, (crun + 1) * 4 + index2); + unsigned int len = OleUtil::getU1Byte(formatPageBuffer, chpxOffset); + unsigned int charPos = 0; + if (!offsetToCharPos(offset, charPos, myPieces)) { + continue; + } + unsigned int styleId = getStyleIdByCharPos(charPos, myStyleInfoList); + + CharInfo charInfo = getStyleFromStylesheet(styleId, myStyleSheet).CurrentCharInfo; + if (chpxOffset != 0) { + getCharInfo(chpxOffset, styleId, formatPageBuffer + 1, len - 1, charInfo); + } + myCharInfoList.push_back(CharPosToCharInfo(charPos, charInfo)); + + if (chpxOffset != 0) { + InlineImageInfo pictureInfo; + if (getInlineImageInfo(chpxOffset, formatPageBuffer + 1, len - 1, pictureInfo)) { + myInlineImageInfoList.push_back(CharPosToInlineImageInfo(charPos, pictureInfo)); + } + } + + } + } + delete[] formatPageBuffer; + return true; +} + +bool OleMainStream::readFloatingImages(const char *headerBuffer, const OleEntry &tableEntry) { + //Plcspa structure is a table with information for FSPA (File Shape Address) + unsigned int beginPicturesInfo = OleUtil::getU4Bytes(headerBuffer, 0x01DA); // address of Plcspa structure + if (beginPicturesInfo == 0) { + return true; //there's no office art objects + } + unsigned int picturesInfoLength = OleUtil::getU4Bytes(headerBuffer, 0x01DE); // length of Plcspa structure + if (picturesInfoLength < 4) { + return false; + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginPicturesInfo, picturesInfoLength, tableStream)) { + return false; + } + + static const unsigned int SPA_SIZE = 26; + std::size_t size = calcCountOfPLC(picturesInfoLength, SPA_SIZE); + + std::vector picturesBlocks; + for (std::size_t index = 0, tOffset = 0; index < size; ++index, tOffset += 4) { + picturesBlocks.push_back(OleUtil::getU4Bytes(buffer.c_str(), tOffset)); + } + + for (std::size_t index = 0, tOffset = (size + 1) * 4; index < size; ++index, tOffset += SPA_SIZE) { + unsigned int spid = OleUtil::getU4Bytes(buffer.c_str(), tOffset); + FloatImageInfo info; + unsigned int charPos = picturesBlocks.at(index); + info.ShapeId = spid; + myFloatImageInfoList.push_back(CharPosToFloatImageInfo(charPos, info)); + } + + //DggInfo structure is office art object table data + unsigned int beginOfficeArtContent = OleUtil::getU4Bytes(headerBuffer, 0x22A); // address of DggInfo structure + if (beginOfficeArtContent == 0) { + return true; //there's no office art objects + } + unsigned int officeArtContentLength = OleUtil::getU4Bytes(headerBuffer, 0x022E); // length of DggInfo structure + if (officeArtContentLength < 4) { + return false; + } + + shared_ptr newTableStream = new OleStream(myStorage, tableEntry, myBaseStream); + shared_ptr newMainStream = new OleStream(myStorage, myOleEntry, myBaseStream); + if (newTableStream->open() && newMainStream->open()) { + myFLoatImageReader = new DocFloatImageReader(beginOfficeArtContent, officeArtContentLength, newTableStream, newMainStream); + myFLoatImageReader->readAll(); + } + return true; +} + +bool OleMainStream::readParagraphStyleTable(const char *headerBuffer, const OleEntry &tableEntry) { + //PlcBtePapx structure is table with formatting for all paragraphs + unsigned int beginParagraphInfo = OleUtil::getU4Bytes(headerBuffer, 0x102); // address of PlcBtePapx structure + std::size_t paragraphInfoLength = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0x106); // length of PlcBtePapx structure + if (paragraphInfoLength < 4) { + return false; + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginParagraphInfo, paragraphInfoLength, tableStream)) { + return false; + } + + static const unsigned int PAPX_SIZE = 4; + std::size_t size = calcCountOfPLC(paragraphInfoLength, PAPX_SIZE); + + std::vector paragraphBlocks; + for (std::size_t index = 0, tOffset = (size + 1) * 4; index < size; ++index, tOffset += PAPX_SIZE) { + paragraphBlocks.push_back(OleUtil::getU4Bytes(buffer.c_str(), tOffset)); + } + + char *formatPageBuffer = new char[OleStorage::BBD_BLOCK_SIZE]; + for (std::size_t index = 0; index < paragraphBlocks.size(); ++index) { + seek(paragraphBlocks.at(index) * OleStorage::BBD_BLOCK_SIZE, true); + if (read(formatPageBuffer, OleStorage::BBD_BLOCK_SIZE) != OleStorage::BBD_BLOCK_SIZE) { + return false; + } + const unsigned int paragraphsCount = OleUtil::getU1Byte(formatPageBuffer, 0x1ff); //offset with 'cpara' value (count of paragraphs) + for (unsigned int index2 = 0; index2 < paragraphsCount; ++index2) { + const unsigned int offset = OleUtil::getU4Bytes(formatPageBuffer, index2 * 4); + unsigned int papxOffset = OleUtil::getU1Byte(formatPageBuffer, (paragraphsCount + 1) * 4 + index2 * 13) * 2; + if (papxOffset <= 0) { + continue; + } + unsigned int len = OleUtil::getU1Byte(formatPageBuffer, papxOffset) * 2; + if (len == 0) { + ++papxOffset; + len = OleUtil::getU1Byte(formatPageBuffer, papxOffset) * 2; + } + + const unsigned int styleId = OleUtil::getU2Bytes(formatPageBuffer, papxOffset + 1); + Style styleInfo = getStyleFromStylesheet(styleId, myStyleSheet); + + if (len >= 3) { + getStyleInfo(papxOffset, formatPageBuffer + 3, len - 3, styleInfo); + } + + unsigned int charPos = 0; + if (!offsetToCharPos(offset, charPos, myPieces)) { + continue; + } + myStyleInfoList.push_back(CharPosToStyle(charPos, styleInfo)); + } + } + delete[] formatPageBuffer; + return true; +} + +bool OleMainStream::readSectionsInfoTable(const char *headerBuffer, const OleEntry &tableEntry) { + //PlcfSed structure is a section table + unsigned int beginOfText = OleUtil::getU4Bytes(headerBuffer, 0x18); //address of text's begin in main stream + unsigned int beginSectInfo = OleUtil::getU4Bytes(headerBuffer, 0xca); //address if PlcfSed structure + + std::size_t sectInfoLen = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0xce); //length of PlcfSed structure + if (sectInfoLen < 4) { + return false; + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginSectInfo, sectInfoLen, tableStream)) { + return false; + } + + static const unsigned int SED_SIZE = 12; + std::size_t decriptorsCount = calcCountOfPLC(sectInfoLen, SED_SIZE); + + //saving the section offsets (in character positions) + std::vector charPos; + for (std::size_t index = 0, tOffset = 0; index < decriptorsCount; ++index, tOffset += 4) { + unsigned int ulTextOffset = OleUtil::getU4Bytes(buffer.c_str(), tOffset); + charPos.push_back(beginOfText + ulTextOffset); + } + + //saving sepx offsets + std::vector sectPage; + for (std::size_t index = 0, tOffset = (decriptorsCount + 1) * 4; index < decriptorsCount; ++index, tOffset += SED_SIZE) { + sectPage.push_back(OleUtil::getU4Bytes(buffer.c_str(), tOffset + 2)); + } + + //reading the section properties + char tmpBuffer[2]; + for (std::size_t index = 0; index < sectPage.size(); ++index) { + if (sectPage.at(index) == 0xffffffffUL) { //check for invalid record, to make default section info + SectionInfo sectionInfo; + sectionInfo.CharPosition = charPos.at(index); + mySectionInfoList.push_back(sectionInfo); + continue; + } + //getting number of bytes to read + if (!seek(sectPage.at(index), true)) { + continue; + } + if (read(tmpBuffer, 2) != 2) { + continue; + } + std::size_t bytes = 2 + (std::size_t)OleUtil::getU2Bytes(tmpBuffer, 0); + + if (!seek(sectPage.at(index), true)) { + continue; + } + char *formatPageBuffer = new char[bytes]; + if (read(formatPageBuffer, bytes) != bytes) { + delete[] formatPageBuffer; + continue; + } + SectionInfo sectionInfo; + sectionInfo.CharPosition = charPos.at(index); + getSectionInfo(formatPageBuffer + 2, bytes - 2, sectionInfo); + mySectionInfoList.push_back(sectionInfo); + delete[] formatPageBuffer; + } + return true; +} + +void OleMainStream::getStyleInfo(unsigned int papxOffset, const char *grpprlBuffer, unsigned int bytes, Style &styleInfo) { + int tmp, toDelete, toAdd; + unsigned int offset = 0; + while (bytes >= offset + 2) { + unsigned int curPrlLength = 0; + switch (OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset)) { + case 0x2403: + styleInfo.Alignment = (Style::AlignmentType)OleUtil::getU1Byte(grpprlBuffer, papxOffset + offset + 2); + break; + case 0x4610: + styleInfo.LeftIndent += OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + if (styleInfo.LeftIndent < 0) { + styleInfo.LeftIndent = 0; + } + break; + case 0xc60d: // ChgTabsPapx + case 0xc615: // ChgTabs + tmp = OleUtil::get1Byte(grpprlBuffer, papxOffset + offset + 2); + if (tmp < 2) { + curPrlLength = 1; + break; + } + toDelete = OleUtil::getU1Byte(grpprlBuffer, papxOffset + offset + 3); + if (tmp < 2 + 2 * toDelete) { + curPrlLength = 1; + break; + } + toAdd = OleUtil::getU1Byte(grpprlBuffer, papxOffset + offset + 4 + 2 * toDelete); + if (tmp < 2 + 2 * toDelete + 2 * toAdd) { + curPrlLength = 1; + break; + } + break; + case 0x840e: + styleInfo.RightIndent = (int)OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0x840f: + styleInfo.LeftIndent = (int)OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0x8411: + styleInfo.FirstLineIndent = (int)OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0xa413: + styleInfo.BeforeParagraphIndent = OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0xa414: + styleInfo.AfterParagraphIndent = OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0x2407: + styleInfo.HasPageBreakBefore = OleUtil::getU1Byte(grpprlBuffer, papxOffset + offset + 2) == 0x01; + break; + default: + break; + } + if (curPrlLength == 0) { + curPrlLength = getPrlLength(grpprlBuffer, papxOffset + offset); + } + offset += curPrlLength; + } + +} + +void OleMainStream::getCharInfo(unsigned int chpxOffset, unsigned int /*styleId*/, const char *grpprlBuffer, unsigned int bytes, CharInfo &charInfo) { + unsigned int sprm = 0; //single propery modifier + unsigned int offset = 0; + while (bytes >= offset + 2) { + switch (OleUtil::getU2Bytes(grpprlBuffer, chpxOffset + offset)) { + case 0x0835: //bold + sprm = OleUtil::getU1Byte(grpprlBuffer, chpxOffset + offset + 2); + switch (sprm) { + case UNSET: + charInfo.FontStyle &= ~CharInfo::FONT_BOLD; + break; + case SET: + charInfo.FontStyle |= CharInfo::FONT_BOLD; + break; + case UNCHANGED: + break; + case NEGATION: + charInfo.FontStyle ^= CharInfo::FONT_BOLD; + break; + default: + break; + } + break; + case 0x0836: //italic + sprm = OleUtil::getU1Byte(grpprlBuffer, chpxOffset + offset + 2); + switch (sprm) { + case UNSET: + charInfo.FontStyle &= ~CharInfo::FONT_ITALIC; + break; + case SET: + charInfo.FontStyle |= CharInfo::FONT_ITALIC; + break; + case UNCHANGED: + break; + case NEGATION: + charInfo.FontStyle ^= CharInfo::FONT_ITALIC; + break; + default: + break; + } + break; + case 0x4a43: //size of font + charInfo.FontSize = OleUtil::getU2Bytes(grpprlBuffer, chpxOffset + offset + 2); + break; + default: + break; + } + offset += getPrlLength(grpprlBuffer, chpxOffset + offset); + } + +} + +void OleMainStream::getSectionInfo(const char *grpprlBuffer, std::size_t bytes, SectionInfo §ionInfo) { + unsigned int tmp; + std::size_t offset = 0; + while (bytes >= offset + 2) { + switch (OleUtil::getU2Bytes(grpprlBuffer, offset)) { + case 0x3009: //new page + tmp = OleUtil::getU1Byte(grpprlBuffer, offset + 2); + sectionInfo.IsNewPage = (tmp != 0 && tmp != 1); + break; + default: + break; + } + offset += getPrlLength(grpprlBuffer, offset); + } +} + +bool OleMainStream::getInlineImageInfo(unsigned int chpxOffset, const char *grpprlBuffer, unsigned int bytes, InlineImageInfo &pictureInfo) { + //p. 105 of [MS-DOC] documentation + unsigned int offset = 0; + bool isFound = false; + while (bytes >= offset + 2) { + switch (OleUtil::getU2Bytes(grpprlBuffer, chpxOffset + offset)) { + case 0x080a: // ole object, p.107 [MS-DOC] + if (OleUtil::getU1Byte(grpprlBuffer, chpxOffset + offset + 2) == 0x01) { + return false; + } + break; + case 0x0806: // is not a picture, but a binary data? (sprmCFData, p.106 [MS-DOC]) + if (OleUtil::getU4Bytes(grpprlBuffer, chpxOffset + offset + 2) == 0x01) { + return false; + } + break; +// case 0x0855: // sprmCFSpec, p.117 [MS-DOC], MUST BE applied with a value of 1 (see p.105 [MS-DOC]) +// if (OleUtil::getU1Byte(grpprlBuffer, chpxOffset + offset + 2) != 0x01) { +// return false; +// } +// break; + case 0x6a03: // location p.105 [MS-DOC] + pictureInfo.DataPosition = OleUtil::getU4Bytes(grpprlBuffer, chpxOffset + offset + 2); + isFound = true; + break; + default: + break; + } + offset += getPrlLength(grpprlBuffer, chpxOffset + offset); + } + return isFound; +} + +OleMainStream::Style OleMainStream::getStyleFromStylesheet(unsigned int styleId, const StyleSheet &stylesheet) { + //TODO optimize it: StyleSheet can be map structure with styleId key + Style style; + if (styleId != Style::STYLE_INVALID && styleId != Style::STYLE_NIL && styleId != Style::STYLE_USER) { + for (std::size_t index = 0; index < stylesheet.size(); ++index) { + if (stylesheet.at(index).StyleIdCurrent == styleId) { + return stylesheet.at(index); + } + } + } + style.StyleIdCurrent = styleId; + return style; +} + +int OleMainStream::getStyleIndex(unsigned int styleId, const std::vector &isFilled, const StyleSheet &stylesheet) { + //TODO optimize it: StyleSheet can be map structure with styleId key + //in that case, this method will be excess + if (styleId == Style::STYLE_INVALID) { + return -1; + } + for (int index = 0; index < (int)stylesheet.size(); ++index) { + if (isFilled.at(index) && stylesheet.at(index).StyleIdCurrent == styleId) { + return index; + } + } + return -1; +} + +unsigned int OleMainStream::getStyleIdByCharPos(unsigned int charPos, const StyleInfoList &styleInfoList) { + unsigned int styleId = Style::STYLE_INVALID; + for (std::size_t i = 0; i < styleInfoList.size(); ++i) { + const Style &info = styleInfoList.at(i).second; + if (i == styleInfoList.size() - 1) { //if last + styleId = info.StyleIdCurrent; + break; + } + unsigned int curOffset = styleInfoList.at(i).first; + unsigned int nextOffset = styleInfoList.at(i + 1).first; + if (charPos >= curOffset && charPos < nextOffset) { + styleId = info.StyleIdCurrent; + break; + } + } + return styleId; +} + +bool OleMainStream::offsetToCharPos(unsigned int offset, unsigned int &charPos, const Pieces &pieces) { + if (pieces.empty()) { + return false; + } + if ((unsigned int)pieces.front().Offset > offset) { + charPos = 0; + return true; + } + if ((unsigned int)(pieces.back().Offset + pieces.back().Length) <= offset) { + return false; + } + + std::size_t pieceNumber = 0; + for (std::size_t i = 0; i < pieces.size(); ++i) { + if (i == pieces.size() - 1) { //if last + pieceNumber = i; + break; + } + unsigned int curOffset = pieces.at(i).Offset; + unsigned int nextOffset = pieces.at(i + 1).Offset; + if (offset >= curOffset && offset < nextOffset) { + pieceNumber = i; + break; + } + } + + const Piece &piece = pieces.at(pieceNumber); + unsigned int diffOffset = offset - piece.Offset; + if (!piece.IsANSI) { + diffOffset /= 2; + } + charPos = piece.startCP + diffOffset; + return true; +} + +bool OleMainStream::readToBuffer(std::string &result, unsigned int offset, std::size_t length, OleStream &stream) { + char *buffer = new char[length]; + stream.seek(offset, true); + if (stream.read(buffer, length) != length) { + return false; + } + result = std::string(buffer, length); + delete[] buffer; + return true; +} + +unsigned int OleMainStream::calcCountOfPLC(unsigned int totalSize, unsigned int elementSize) { + //calculates count of elements in PLC structure, formula from p.30 [MS-DOC] + return (totalSize - 4) / (4 + elementSize); +} + +unsigned int OleMainStream::getPrlLength(const char *grpprlBuffer, unsigned int byteNumber) { + unsigned int tmp; + unsigned int opCode = OleUtil::getU2Bytes(grpprlBuffer, byteNumber); + switch (opCode & 0xe000) { + case 0x0000: + case 0x2000: + return 3; + case 0x4000: + case 0x8000: + case 0xA000: + return 4; + case 0xE000: + return 5; + case 0x6000: + return 6; + case 0xC000: + //counting of info length + tmp = OleUtil::getU1Byte(grpprlBuffer, byteNumber + 2); + if (opCode == 0xc615 && tmp == 255) { + unsigned int del = OleUtil::getU1Byte(grpprlBuffer, byteNumber + 3); + unsigned int add = OleUtil::getU1Byte(grpprlBuffer, byteNumber + 4 + del * 4); + tmp = 2 + del * 4 + add * 3; + } + return 3 + tmp; + default: + return 1; + } +} diff --git a/reader/src/formats/doc/OleMainStream.h b/reader/src/formats/doc/OleMainStream.h new file mode 100644 index 0000000..378f037 --- /dev/null +++ b/reader/src/formats/doc/OleMainStream.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2004-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. + */ + +#ifndef __OLEMAINSTREAM_H__ +#define __OLEMAINSTREAM_H__ + +#include +#include + +#include "OleStream.h" +#include "DocFloatImageReader.h" + +class OleMainStream : public OleStream { + +public: + struct Piece { + enum PieceType { + PIECE_TEXT, + PIECE_FOOTNOTE, + PIECE_OTHER + }; + + int Offset; // TODO: maybe make it unsigned int + int Length; // TODO: maybe make it unsigned int + bool IsANSI; + PieceType Type; + unsigned int startCP; + }; + typedef std::vector Pieces; + + struct CharInfo { + enum Font { + FONT_REGULAR = 0, + FONT_BOLD = 1 << 0, + FONT_ITALIC = 1 << 1, + FONT_UNDERLINE = 1 << 2, + FONT_CAPITALS = 1 << 3, + FONT_SMALL_CAPS = 1 << 4, + FONT_STRIKE = 1 << 5, + FONT_HIDDEN = 1 << 6, + FONT_MARKDEL = 1 << 7, + FONT_SUPERSCRIPT = 1 << 8, + FONT_SUBSCRIPT = 1 << 9 + }; + + unsigned int FontStyle; + unsigned int FontSize; + + CharInfo(); + }; + typedef std::pair CharPosToCharInfo; + typedef std::vector CharInfoList; + + struct Style { + enum AlignmentType { + ALIGNMENT_LEFT = 0x00, + ALIGNMENT_CENTER = 0x01, + ALIGNMENT_RIGHT = 0x02, + ALIGNMENT_JUSTIFY = 0x03, + ALIGNMENT_DEFAULT // for case if alignment is not setted by word + }; + + // style Ids: + // (this is not full list of possible style ids, enum is used for using in switch-case) + enum StyleID { + STYLE_H1 = 0x1, + STYLE_H2 = 0x2, + STYLE_H3 = 0x3, + STYLE_USER = 0xFFE, + STYLE_NIL = 0xFFF, + STYLE_INVALID = 0xFFFF + }; + + unsigned int StyleIdCurrent; + unsigned int StyleIdNext; // Next style unless overruled + + bool HasPageBreakBefore; + unsigned int BeforeParagraphIndent; // Vertical indent before paragraph, pixels + unsigned int AfterParagraphIndent; // Vertical indent after paragraph, pixels + int LeftIndent; + int FirstLineIndent; + int RightIndent; + AlignmentType Alignment; + CharInfo CurrentCharInfo; + + Style(); + }; + + typedef std::pair CharPosToStyle; + typedef std::vector StyleInfoList; + typedef std::vector