public BriefBookInfo(FictionBook fictionBook, Fb2DocumentEntry documentEntry)
        {
            if (fictionBook == null)
            {
                throw new ArgumentNullException("fictionBook");
            }

            if (documentEntry == null)
            {
                throw new ArgumentNullException("documentEntry");
            }

            this.fictionBook = fictionBook;
            this.documentEntry = documentEntry;
        }
        internal XmlFb2DocumentEntry(Fb2DocumentEntry documentEntry)
        {
            if (documentEntry == null)
            {
                throw new ArgumentNullException("documentEntry");
            }

            bookid = documentEntry.BookId;
            status = documentEntry.Status;
            filename = documentEntry.Filename;
            originalFilename = documentEntry.OriginalFileName;
            filedate = documentEntry.FileDate;
            filesize = documentEntry.FileSize;

            errorText = !String.IsNullOrEmpty(documentEntry.ErrorText) ? invalidChars.Replace(documentEntry.ErrorText, " ") : null;
        }
Exemple #3
0
        private BookAction CheckForDuplicate(FictionBook fictionBook, Fb2DocumentEntry documentEntry, IEnumerable<BookInfo> list)
        {
            BookAction action = BookAction.None;

            if(fictionBook.DocumentInfo == null)
            {
                throw new InvalidFictionBookFormatException("Missing required element: document-info");
            }

            ImportStatus checkStatus = ImportStatus.None;

            foreach (BookInfo book in list)
            {
                string srcBookTitle = this.titleRegex.Replace(fictionBook.TitleInfo.BookTitle, "");
                string dstBookTitle = this.titleRegex.Replace(book.BookTitle, "");

                if (String.Compare(srcBookTitle, dstBookTitle, true) == 0)
                {
                    if (String.Compare(book.Id, fictionBook.DocumentInfo.Id) == 0)
                    {
                        float version = float.Parse(book.Version, CultureInfo.InvariantCulture);
                        float documentVersion = fictionBook.DocumentInfo.Version ?? 0;

                        if (documentVersion > version)
                        {
                            checkStatus = ImportStatus.Updated;
                            documentEntry.BookId = book.BookId;
                            action = BookAction.Update;
                            break;
                        }

                        checkStatus = documentVersion < version ? ImportStatus.DuplicateOlder : ImportStatus.Duplicate;
                    }
                    else
                    {
                        checkStatus = ImportStatus.DuplicateIDsDiffer;
                    }
                }
                else
                {
                    string bookTitle = fictionBook.TitleInfo.BookTitle;

                    int distance = StringUtils.DamerauLevenshteinDistance(bookTitle, book.BookTitle);
                    double metric = (1 - (double)distance / bookTitle.Length) * 100.0;

                    if (metric > 70)
                    {
                        metric = StringUtils.LongestCommonSubstring(bookTitle, book.BookTitle) * 100.0 / bookTitle.Length;

                        if(metric > 80)
                        {
                            continue;
                        }

                        if (!String.IsNullOrEmpty(book.Sequence) && !String.IsNullOrEmpty(book.SequenceNumber))
                        {
                            string sequence = null;
                            string sequenceNumber = null;

                            foreach (SequenceInfoNode sequenceInfo in fictionBook.TitleInfo.Sequences)
                            {
                                sequence = StringUtils.Truncate(sequenceInfo.Name, 125);
                                if (sequenceInfo.Number != null)
                                {
                                    sequenceNumber = sequenceInfo.Number.Value.ToString("000", CultureInfo.InvariantCulture);
                                }

                                break;
                            }

                            if(!String.IsNullOrEmpty(sequenceNumber))
                            {
                                if (String.Compare(book.Sequence, sequence) == 0 && String.Compare(book.SequenceNumber, sequenceNumber) != 0)
                                {
                                    continue;
                                }
                            }
                        }

                        if (String.Compare(book.Id, fictionBook.DocumentInfo.Id) == 0)
                        {
                            checkStatus = ImportStatus.Duplicate;
                        }
                        else
                        {
                            checkStatus = ImportStatus.DuplicateIDsDiffer;
                        }
                    }
                }

                if (IsDuplicate(checkStatus))
                {
                    documentEntry.Status = checkStatus;
                    documentEntry.BookId = book.BookId;

                    if (checkStatus == ImportStatus.Duplicate)
                    {
                        if (documentEntry.FileDate > book.FileDate)
                        {
                            documentEntry.Status = ImportStatus.DuplicateNewer;
                        }
                        else if (documentEntry.FileDate < book.FileDate)
                        {
                            documentEntry.Status = ImportStatus.DuplicateOlder;
                        }
                    }

                    break;
                }
            }

            if (checkStatus == ImportStatus.None)
            {
                action = BookAction.Add;
            }

            return action;
        }
Exemple #4
0
        private ImportStatus ProcessDocument(Fb2DocumentEntry documentEntry, Stream stream, XmlDocument document, Encoding encoding)
        {
            ImportStatus importResult;
            FictionBook fictionBook;

            try
            {
                importResult = documentEntry.Status;

                fictionBook = new FictionBook(document, encoding);

                if(filter != null && !filter.Fit(new FilterAdapter(fictionBook)))
                {
                    documentEntry.Status = ImportStatus.FilteredOut;
                    this.processLegend.IncrementCounter(documentEntry.Status);

                    return documentEntry.Status;
                }

                BookAction action = BookAction.None;

                if (!dontCheckForDuplicate)
                {
                    Set<int> simularAuthors = new Set<int>();

                    foreach (AuthorInfoNode authorInfoNode in fictionBook.TitleInfo.Authors)
                    {
                        List<Author> list = this.database.FindSimularAuthors(authorInfoNode);
                        simularAuthors.AddRange(list.ConvertAll(delegate(Author author)
                        {
                            return author.Id ?? 0;
                        }));
                    }

                    List<BookInfo> books = this.database.LoadBookInfoByDocumentId(fictionBook.DocumentInfo.Id);
                    action = CheckForDuplicate(fictionBook, documentEntry, books);

                    if (action == BookAction.Add)
                    {
                        List<BookInfo> simularBooks = this.database.LoadBookInfoByAuthorIdList(simularAuthors);
                        action = CheckForDuplicate(fictionBook, documentEntry, simularBooks);
                    }
                }
                else
                {
                    action = BookAction.Add;
                }

                stream.Seek(0, SeekOrigin.Begin);

                BookInfo bookInfo;

                switch (action)
                {
                    case BookAction.Add:
                        importResult = ImportStatus.Added;

                        bookInfo = this.database.CreateFictionBook(fictionBook, stream, documentEntry);
                        documentEntry.BookId = bookInfo.BookId;
                        documentEntry.Status = ImportStatus.Added;
                        break;

                    case BookAction.Update:
                        importResult = ImportStatus.Updated;

                        bookInfo = this.database.LoadBookInfoByBookId(documentEntry.BookId ?? -1);
                        this.database.UpdateFictionBook(bookInfo, fictionBook, stream, documentEntry);

                        documentEntry.BookId = bookInfo.BookId;
                        documentEntry.Status = ImportStatus.Updated;
                        break;

                    case BookAction.None:
                        importResult = documentEntry.Status;
                        break;
                }

                switch (documentEntry.Status)
                {
                    case ImportStatus.Added:
                    case ImportStatus.Updated:
                    case ImportStatus.Duplicate:
                    case ImportStatus.DuplicateIDsDiffer:
                    case ImportStatus.DuplicateNewer:
                    case ImportStatus.DuplicateOlder:
                        this.processLegend.IncrementCounter(documentEntry.Status);
                        break;
                }
            }
            catch(DatabaseException exp)
            {
                documentEntry.ErrorText = exp.Message;

                this.processLegend.IncrementCounter(ImportStatus.DatabaseError);
                importResult = ImportStatus.DatabaseError;
            }
            catch (InvalidFictionBookFormatException exp)
            {
                documentEntry.ErrorText = exp.Message;

                this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                importResult = ImportStatus.ParsingError;
            }

            return importResult;
        }
Exemple #5
0
        private ImportStatus ProcessDocument(Fb2DocumentEntry documentEntry, Stream stream)
        {
            ImportStatus importResult;

            try
            {
                using (XmlTextReader reader = new XmlTextReader(new StreamWrapper(stream)))
                {
                    XmlDocument document = new XmlDocument();
                    document.Load(reader);

                    importResult = ProcessDocument(documentEntry, stream, document, reader.Encoding);
                }
            }
            catch (XmlException exp)
            {
                documentEntry.ErrorText = exp.Message;

                this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                importResult = ImportStatus.ParsingError;
            }
            catch (IOException exp)
            {
                documentEntry.ErrorText = exp.Message;

                this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                importResult = ImportStatus.ParsingError;
            }

            return importResult;
        }
Exemple #6
0
 private ImportStatus ProcessDocument(Fb2DocumentEntry documentEntry, String filename)
 {
     using (FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
     {
         return ProcessDocument(documentEntry, stream);
     }
 }
Exemple #7
0
        private void Process(Fb2DocumentEntry documentEntry)
        {
            IFileObject fileObject = manager.resolveFile(documentEntry.Filename);

            ImportFileEntry entry = null;
            if(excludeList.ContainsKey(documentEntry.Filename))
            {
                entry = excludeList[documentEntry.Filename];
            }

            try
            {
                try
                {
                    this.processLegend.DecrementCounter(documentEntry.Status);

                    using (Stream stream = fileObject.Content.InputStream)
                    {
                        try
                        {
                            if (stream.CanSeek)
                            {
                                documentEntry.Status = ProcessDocument(documentEntry, stream);
                            }
                            else
                            {
                                using (MemoryStream memoryStream = new MemoryStream((int)documentEntry.FileSize))
                                {
                                    byte[] buffer = bufferPool.Aquire();

                                    try
                                    {
                                        StreamUtils.Copy(stream, memoryStream, buffer);
                                    }
                                    finally
                                    {
                                        bufferPool.Release(buffer);
                                    }

                                    memoryStream.Seek(0, SeekOrigin.Begin);

                                    documentEntry.Status = ProcessDocument(documentEntry, stream);
                                }
                            }

                        }
                        finally
                        {
                            stream.Close();
                        }
                    }

                    if(entry != null)
                    {
                        entry.Status = documentEntry.Status;
                    }
                }
                catch (Exception exp)
                {
                    Logger.WriteError(exp.Message);
                    Logger.WriteLine(TraceEventType.Verbose, exp);

                    documentEntry.Status = ImportStatus.ParsingError;
                    documentEntry.ErrorText = exp.Message;

                    if (entry != null)
                    {
                        entry.Status = documentEntry.Status;
                    }

                    this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                }
            }
            finally
            {
                manager.closeFileSystem(fileObject.FileSystem);
            }

            if (entry != null)
            {
                switch (documentEntry.Status)
                {
                    case ImportStatus.Added:
                    case ImportStatus.FilteredOut:
                    case ImportStatus.Updated:
                    case ImportStatus.Duplicate:
                        if (entry.Selected)
                        {
                            this.processLegend.DecrementCounter(ImportStatus.ReadyToProcess);
                            entry.Selected = false;
                        }
                        break;
                }
            }

            this.grdResult.RefreshDataSource();
            this.grdLegend.RefreshDataSource();

            Application.DoEvents();
        }
Exemple #8
0
        private void Process(ImportFileEntry entry)
        {
            Fb2DocumentEntry documentEntry = null;

            try
            {
                IFileObject fileObject = manager.resolveFile(entry.Uri);

                documentEntry = new Fb2DocumentEntry();
                documentEntry.OriginalFileName = entry.DisplayText;
                documentEntry.Filename = entry.Uri;
                documentEntry.FileDate = new DateTime(fileObject.Content.LastModifiedTime);
                documentEntry.FileSize = fileObject.Content.Size;

                try
                {
                    try
                    {
                        processLog.Add(documentEntry);

                        using (Stream stream = fileObject.Content.InputStream)
                        {
                            try
                            {
                                if (stream.CanSeek)
                                {
                                    documentEntry.Status = ProcessDocument(documentEntry, stream);
                                }
                                else
                                {
                                    using (MemoryStream memoryStream = new MemoryStream((int)documentEntry.FileSize))
                                    {
                                        byte[] buffer = bufferPool.Aquire();

                                        try
                                        {
                                            StreamUtils.Copy(stream, memoryStream, buffer);
                                        }
                                        finally
                                        {
                                            bufferPool.Release(buffer);
                                        }

                                        memoryStream.Seek(0, SeekOrigin.Begin);

                                        documentEntry.Status = ProcessDocument(documentEntry, stream);
                                    }
                                }

                            }
                            finally
                            {
                                stream.Close();
                            }
                        }

                        entry.Status = documentEntry.Status;
                    }
                    catch (Exception exp)
                    {
                        Logger.WriteError(exp.Message);
                        Logger.WriteLine(TraceEventType.Verbose, exp);

                        documentEntry.Status = ImportStatus.ParsingError;
                        documentEntry.ErrorText = exp.Message;

                        entry.Status = ImportStatus.ParsingError;

                        this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                    }
                }
                finally
                {
                    manager.closeFileSystem(fileObject.FileSystem);
                }

                this.grdResult.RefreshDataSource();

                switch (documentEntry.Status)
                {
                    case ImportStatus.Added:
                    case ImportStatus.FilteredOut:
                    case ImportStatus.Updated:
                    case ImportStatus.Duplicate:
                        entry.Selected = false;
                        this.processLegend.DecrementCounter(ImportStatus.ReadyToProcess);
                        break;
                }
            }
            catch (FileSystemException exp)
            {
                Logger.WriteError(exp.Message);
                Logger.WriteLine(TraceEventType.Verbose, exp);

                if (documentEntry != null)
                {
                    documentEntry.Status = ImportStatus.ArchiveError;
                    documentEntry.ErrorText = exp.Message;
                }

                entry.Status = ImportStatus.ArchiveError;

                this.processLegend.IncrementCounter(ImportStatus.ArchiveError);
            }

            this.grdSelectedFiles.RefreshDataSource();
            this.grdLegend.RefreshDataSource();

            Application.DoEvents();
        }
Exemple #9
0
        private BriefBookInfo LoadBriefBookInfo(Fb2DocumentEntry documentEntry)
        {
            try
            {
                IFileObject fileObject = manager.resolveFile(documentEntry.Filename);

                try
                {
                    using (Stream stream = fileObject.Content.InputStream)
                    {
                        try
                        {
                            using (XmlTextReader reader = new XmlTextReader(new StreamWrapper(stream)))
                            {
                                XmlDocument document = new XmlDocument();
                                document.Load(reader);

                                FictionBook fictionBook = new FictionBook(document, reader.Encoding);
                                return new BriefBookInfo(fictionBook, documentEntry);
                            }

                        }
                        finally
                        {
                            stream.Close();
                        }
                    }
                }
                catch
                {
                    return null;
                }
                finally
                {
                    manager.closeFileSystem(fileObject.FileSystem);
                }
            }
            catch (FileSystemException)
            {
            }

            return null;
        }
Exemple #10
0
        private void FocusedRowChanged(Fb2DocumentEntry entry)
        {
            if (entry == null)
            {
                this.cmdReprocessItem.Enabled = false;
                this.cmdReplaceBook.Enabled = false;
                this.cmdCreateNewBook.Enabled = false;

                return;
            }

            switch (entry.Status)
            {
                case ImportStatus.Added:
                    this.LegendViewMode = LegendViewMode.BookInfo;

                    this.briefBookInfo1.DataSource = LoadBriefBookInfo(entry.BookId ?? -1);
                    break;

                case ImportStatus.FilteredOut:
                    this.LegendViewMode = LegendViewMode.BookInfo;

                    this.briefBookInfo1.DataSource = LoadBriefBookInfo(entry);
                    break;

                case ImportStatus.Duplicate:
                case ImportStatus.DuplicateIDsDiffer:
                case ImportStatus.DuplicateNewer:
                case ImportStatus.DuplicateOlder:
                    this.LegendViewMode = LegendViewMode.ConflictInfo;

                    this.briefBookInfo1.DataSource = LoadBriefBookInfo(entry.BookId ?? -1);
                    this.briefBookInfo2.DataSource = LoadBriefBookInfo(entry);
                    break;

                case ImportStatus.ArchiveError:
                case ImportStatus.DatabaseError:
                case ImportStatus.ParsingError:
                    this.LegendViewMode = LegendViewMode.ErrorInfo;

                    this.emptySpaceItem1.Text = entry.ErrorText;
                    this.emptySpaceItem1.TextVisible = true;
                    break;

                default:
                    this.LegendViewMode = LegendViewMode.None;
                    this.emptySpaceItem1.TextVisible = false;
                    break;
            }

            switch (this.grvResult.SelectedRowsCount)
            {
                case 0:
                    this.cmdReprocessItem.Enabled = false;
                    this.cmdReplaceBook.Enabled = false;
                    this.cmdCreateNewBook.Enabled = false;

                    this.cmdViewSourceBook.Enabled = false;
                    this.cmdViewLibraryBook.Enabled = false;
                    break;

                case 1:
                    switch (entry.Status)
                    {
                        case ImportStatus.FilteredOut:
                            this.cmdReprocessItem.Enabled = true;
                            this.cmdReplaceBook.Enabled = false;
                            this.cmdCreateNewBook.Enabled = false;

                            this.cmdViewSourceBook.Enabled = true;
                            this.cmdViewLibraryBook.Enabled = false;
                            break;

                        case ImportStatus.ReadyToProcess:
                        case ImportStatus.ArchiveError:
                        case ImportStatus.DatabaseError:
                        case ImportStatus.ParsingError:
                            this.cmdReprocessItem.Enabled = true;
                            this.cmdReplaceBook.Enabled = false;
                            this.cmdCreateNewBook.Enabled = false;

                            this.cmdViewSourceBook.Enabled = false;
                            this.cmdViewLibraryBook.Enabled = false;
                            break;

                        case ImportStatus.Duplicate:
                        case ImportStatus.DuplicateIDsDiffer:
                        case ImportStatus.DuplicateNewer:
                        case ImportStatus.DuplicateOlder:
                            this.cmdReprocessItem.Enabled = true;
                            this.cmdReplaceBook.Enabled = true;
                            this.cmdCreateNewBook.Enabled = true;

                            this.cmdViewSourceBook.Enabled = true;
                            this.cmdViewLibraryBook.Enabled = true;
                            break;

                        case ImportStatus.Added:
                        case ImportStatus.Updated:
                            this.cmdReprocessItem.Enabled = false;
                            this.cmdReplaceBook.Enabled = false;
                            this.cmdCreateNewBook.Enabled = false;

                            this.cmdViewSourceBook.Enabled = false;
                            this.cmdViewLibraryBook.Enabled = true;
                            break;

                        default:
                            this.cmdReprocessItem.Enabled = false;
                            this.cmdReplaceBook.Enabled = false;
                            this.cmdCreateNewBook.Enabled = false;

                            this.cmdViewSourceBook.Enabled = false;
                            this.cmdViewLibraryBook.Enabled = false;
                            break;
                    }
                    break;

                default:
                    this.cmdReprocessItem.Enabled = true;
                    this.cmdReplaceBook.Enabled = true;
                    this.cmdCreateNewBook.Enabled = true;

                    this.cmdViewSourceBook.Enabled = false;
                    this.cmdViewLibraryBook.Enabled = false;
                    break;
            }
        }
Exemple #11
0
        private void CreateOrReplace(Fb2DocumentEntry documentEntry, BookAction action)
        {
            IFileObject fileObject = manager.resolveFile(documentEntry.Filename);

            ImportFileEntry entry = null;
            if (excludeList.ContainsKey(documentEntry.Filename))
            {
                entry = excludeList[documentEntry.Filename];
            }

            try
            {
                try
                {
                    this.processLegend.DecrementCounter(documentEntry.Status);

                    using (Stream stream = fileObject.Content.InputStream)
                    {
                        try
                        {
                            documentEntry.Status = CreateOrReplace(documentEntry, action, stream);
                        }
                        finally
                        {
                            stream.Close();
                        }
                    }

                    if (entry != null)
                    {
                        entry.Status = documentEntry.Status;
                    }
                }
                catch (Exception exp)
                {
                    Logger.WriteError(exp.Message);
                    Logger.WriteLine(TraceEventType.Verbose, exp);

                    documentEntry.Status = ImportStatus.ParsingError;
                    documentEntry.ErrorText = exp.Message;

                    if (entry != null)
                    {
                        entry.Status = documentEntry.Status;
                    }

                    this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                }
            }
            finally
            {
                manager.closeFileSystem(fileObject.FileSystem);
            }

            this.grdResult.RefreshDataSource();
            this.grdLegend.RefreshDataSource();

            Application.DoEvents();
        }
Exemple #12
0
        private ImportStatus CreateOrReplace(Fb2DocumentEntry documentEntry, BookAction action, Stream stream)
        {
            ImportStatus importResult;
            FictionBook fictionBook;

            try
            {
                importResult = documentEntry.Status;

                using (MemoryStream memoryStream = new MemoryStream((int)documentEntry.FileSize))
                {
                    byte[] buffer = bufferPool.Aquire();

                    try
                    {
                        StreamUtils.Copy(stream, memoryStream, buffer);
                    }
                    finally
                    {
                        bufferPool.Release(buffer);
                    }

                    memoryStream.Seek(0, SeekOrigin.Begin);

                    using (XmlTextReader reader = new XmlTextReader(memoryStream))
                    {
                        XmlDocument document = new XmlDocument();
                        document.Load(reader);

                        fictionBook = new FictionBook(document, reader.Encoding);

                        memoryStream.Seek(0, SeekOrigin.Begin);

                        switch(action)
                        {
                            case BookAction.Add:
                                BookInfo bookInfo = this.database.CreateFictionBook(fictionBook, memoryStream, documentEntry);
                                documentEntry.BookId = bookInfo.BookId;
                                importResult = ImportStatus.Added;
                                break;

                            case BookAction.Update:
                                bookInfo = database.LoadBookInfoByBookId(documentEntry.BookId ?? -1);
                                this.database.UpdateFictionBook(bookInfo, fictionBook, memoryStream, documentEntry);
                                importResult = ImportStatus.Updated;
                                break;

                        }

                        this.processLegend.IncrementCounter(importResult);
                    }
                }
            }
            catch (DatabaseException exp)
            {
                documentEntry.ErrorText = exp.Message;

                this.processLegend.IncrementCounter(ImportStatus.DatabaseError);
                importResult = ImportStatus.DatabaseError;
            }
            catch (InvalidFictionBookFormatException exp)
            {
                documentEntry.ErrorText = exp.Message;

                this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                importResult = ImportStatus.ParsingError;
            }
            catch (XmlException exp)
            {
                documentEntry.ErrorText = exp.Message;

                this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                importResult = ImportStatus.ParsingError;
            }
            catch (IOException exp)
            {
                documentEntry.ErrorText = exp.Message;

                this.processLegend.IncrementCounter(ImportStatus.ParsingError);
                importResult = ImportStatus.ParsingError;
            }

            return importResult;
        }
        public BookInfo CreateFictionBook(FictionBook fictionBook, Stream content, Fb2DocumentEntry documentEntry, IDbTransaction transaction)
        {
            if (fictionBook == null)
            {
                throw new ArgumentNullException("fictionBook");
            }

            this.manager.BeginConnect();

            try
            {
                DatabaseInfo info = this.DatabaseInfo;

                List<Author> bookAuthors = new List<Author>(fictionBook.TitleInfo.Authors.Count);
                List<Author> documentAuthors = new List<Author>(fictionBook.DocumentInfo.Authors.Count);
                List<Author> bookTranslators = new List<Author>(fictionBook.TitleInfo.Translators.Count);
                List<SequenceInfo> bookSequences = new List<SequenceInfo>(fictionBook.TitleInfo.Sequences.Count);
                List<Genre> bookGenres = new List<Genre>(fictionBook.TitleInfo.Genres.Count);

                foreach (AuthorInfoNode authorInfoNode in fictionBook.TitleInfo.Authors)
                {
                    Author author = this.FindOrCreateAuthor(authorInfoNode, transaction);
                    if(author != null)
                    {
                        bookAuthors.Add(author);
                    }
                }

                foreach (AuthorInfoNode authorInfoNode in fictionBook.DocumentInfo.Authors)
                {
                    Author author = this.FindOrCreateDocumentAuthor(authorInfoNode, transaction);
                    if(author != null)
                    {
                        documentAuthors.Add(author);
                    }
                }

                foreach (AuthorInfoNode authorInfoNode in fictionBook.TitleInfo.Translators)
                {
                    Author translator = this.FindOrCreateTranslator(authorInfoNode, transaction);
                    if(translator != null)
                    {
                        bookTranslators.Add(translator);
                    }
                }

                foreach (SequenceInfoNode sequenceInfoNode in fictionBook.TitleInfo.Sequences)
                {
                    SequenceInfo sequence = this.FindOrCreateBookSequence(sequenceInfoNode, transaction);
                    if(sequence != null)
                    {
                        bookSequences.Add(sequence);
                    }
                }

                Set<Genre> genres = new Set<Genre>(fictionBook.TitleInfo.Genres.Count);
                foreach (GenreInfoNode genreInfoNode in fictionBook.TitleInfo.Genres)
                {
                    string genreName = genreInfoNode.Genre;
                    if (GenreTable.Table.MapTable.ContainsKey(genreName))
                    {
                        genreName = GenreTable.Table.MapTable[genreName];
                    }

                    Genre bookGenre = GenreTable.Table[genreName];
                    if(bookGenre != null)
                    {
                        genres.Add(bookGenre);
                    }
                }

                bookGenres.AddRange(genres);

                BookInfo book = new BookInfo();
                book.Genrelist = StringUtils.Truncate(StringUtils.Join(", ", bookGenres), 1024);

                using (StringWriter writer = new StringWriter())
                {
                    XsltArgumentList arguments = new XsltArgumentList();
                    arguments.AddParam("nodeType", "", "annotation");
                    this.xslt.Transform(fictionBook.Document, arguments, writer);
                    book.Annotation = StringUtils.Truncate(writer.ToString(), 4096);
                }

                book.AuthorList = StringUtils.Truncate(StringUtils.Join("; ", bookAuthors), 254);
                book.Keywords = StringUtils.Truncate(fictionBook.TitleInfo.Keywords, 254);
                book.BookTitle = StringUtils.Truncate(fictionBook.TitleInfo.BookTitle, 252);

                if (fictionBook.TitleInfo.DateNode != null)
                {
                    book.DateValue = fictionBook.TitleInfo.DateNode.Value;
                    book.DateText = StringUtils.Truncate(fictionBook.TitleInfo.DateNode.DateString, 25);
                }

                book.Lang = StringUtils.Truncate(fictionBook.TitleInfo.Lang, 10);
                book.SrcLang = StringUtils.Truncate(fictionBook.TitleInfo.SourceLang, 10);

                foreach (SequenceInfo sequenceInfo in bookSequences)
                {
                    book.SequenceId = sequenceInfo.SequenceId;
                    book.Sequence = StringUtils.Truncate(sequenceInfo.SequenceName, 125);
                    if (sequenceInfo.SequenceNumber != null)
                    {
                        book.SequenceNumber = sequenceInfo.SequenceNumber.Value.ToString("000", CultureInfo.InvariantCulture);
                    }

                    break;
                }

                book.ProgrammUsed = StringUtils.Truncate(fictionBook.DocumentInfo.ProgramUsed ?? String.Empty, 254);

                if (fictionBook.DocumentInfo.DateNode != null)
                {
                    book.DocumentDateValue = fictionBook.DocumentInfo.DateNode.Value;
                    book.DocumentDateText = StringUtils.Truncate(fictionBook.DocumentInfo.DateNode.DateString ?? String.Empty, 14);
                }
                else
                {
                    book.DocumentDateValue = null;
                    book.DocumentDateText = String.Empty;
                }

                book.SrcUrl = StringUtils.Truncate(StringUtils.Join(", ", fictionBook.DocumentInfo.SourceUrl), 254);
                book.SrcOcr = StringUtils.Truncate(fictionBook.DocumentInfo.SourceOCR ?? String.Empty, 254);
                book.Id = StringUtils.Truncate(fictionBook.DocumentInfo.Id, 254);
                book.Version = DocumentInfoNode.FormatVersion(fictionBook.DocumentInfo.Version ?? 0.0f);

                using (StringWriter writer = new StringWriter())
                {
                    XsltArgumentList arguments = new XsltArgumentList();
                    arguments.AddParam("nodeType", "", "history");
                    this.xslt.Transform(fictionBook.Document, arguments, writer);
                    book.History = writer.ToString();
                }

                if (fictionBook.PublishInfo != null)
                {
                    book.BookName = StringUtils.Truncate(fictionBook.PublishInfo.BookName ?? String.Empty, 254);
                    book.Publisher = StringUtils.Truncate(fictionBook.PublishInfo.Publisher ?? String.Empty, 254);
                    book.City = StringUtils.Truncate(fictionBook.PublishInfo.City ?? String.Empty, 50);

                    if (fictionBook.PublishInfo.Year != null)
                    {
                        book.Year = StringUtils.Truncate(fictionBook.PublishInfo.Year.ToString(), 10);
                    }
                    else
                    {
                        book.Year = String.Empty;
                    }

                    book.Isbn = StringUtils.Truncate(fictionBook.PublishInfo.ISBN ?? String.Empty, 125);
                }
                else
                {
                    book.BookName = String.Empty;
                    book.Publisher = String.Empty;
                    book.City = String.Empty;
                    book.Year = String.Empty;
                    book.Isbn = String.Empty;
                }

                using (StringWriter writer = new StringWriter())
                {
                    XsltArgumentList arguments = new XsltArgumentList();
                    arguments.AddParam("nodeType", "", "custom-info");
                    this.xslt.Transform(fictionBook.Document, arguments, writer);
                    book.CustomInfo = writer.ToString();
                }

                book.DateInserted = DateTime.Now;
                book.DateUpdated = null;
                book.Extension = "FB2";

                book.FileDate = documentEntry.FileDate ?? DateTime.Now;
                book.FileSize = documentEntry.FileSize;

                book.SurrogateId = Regex.Replace(String.Concat(book.BookTitle, book.AuthorList), @"[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}]", "").Trim().ToUpperInvariant();

                book.Userid = info.CurrentUserId;
                book.Username = info.CurrentUserName;

                string commandText =
                    " INSERT INTO BOOK " +
                    " ( " +
                    "   BOOKID, GENRELIST, ANNOTATION, AUTORLIST, KEYWORDS, BOOKNAME, DATEVALUE, DATEVISIBLE, " +
                    "   LANG, COVERPAGE, SRCLANG, \"SEQUENCE\", SEQNUMBER, SEQUENCEID, DI_PROGUSED, " +
                    "   DI_DATEVALUE, DI_DATEVISIBLE, DI_SRCURL, DI_SRCOCR, OLDID, DI_VERSION, DI_HISTORY, " +
                    "   PI_BOOKNAME, PI_PUBLISHER, PI_CITY, PI_YEAR, PI_ISBN, CUSTOMINFO, DATEIN, " +
                    "   DATEUPDATED, MYID, EXT, FILESIZE, FILENAME, FILEPATH, FILEDATE, USERID, USERNAME " +
                    " ) " +
                    " VALUES " +
                    " ( " +
                    "   GEN_ID(GEN_BOOK_ID,1), @genrelist, @annotation, @autorlist, @keywords, @bookname, @datevalue, " +
                    "   @datevisible, @lang, @coverpage, @srclang, @sequencename, @seqnumber, @sequenceid, " +
                    "   @di_progused, @di_datevalue, @di_datevisible, @di_srcurl, @di_srcocr, @oldid, " +
                    "   @di_version, @di_history, @pi_bookname, @pi_publisher, @pi_city, @pi_year, @pi_isbn, " +
                    "   @custominfo, @datein, @dateupdated, @myid, @ext, @filesize, @filename, @filepath, " +
                    "   @filedate, @userid, @username " +
                    " ) RETURNING BOOKID";

                using (FbCommand command = this.connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;
                    command.CommandText = commandText;
                    command.Transaction = transaction as FbTransaction;

                    command.Parameters.Add("@genrelist", FbDbType.VarChar).Value = book.Genrelist;
                    command.Parameters.Add("@annotation", FbDbType.VarChar).Value = book.Annotation;
                    command.Parameters.Add("@autorlist", FbDbType.VarChar).Value = book.AuthorList;
                    command.Parameters.Add("@keywords", FbDbType.VarChar).Value = book.Keywords;
                    command.Parameters.Add("@bookname", FbDbType.VarChar).Value = book.BookTitle;
                    command.Parameters.Add("@datevalue", FbDbType.Date).Value = book.DateValue;
                    command.Parameters.Add("@datevisible", FbDbType.VarChar).Value = book.DateText;
                    command.Parameters.Add("@lang", FbDbType.VarChar).Value = book.Lang;
                    command.Parameters.Add("@coverpage", FbDbType.Binary).Value = fictionBook.CoverpageImage;
                    command.Parameters.Add("@srclang", FbDbType.VarChar).Value = book.SrcLang;
                    command.Parameters.Add("@sequencename", FbDbType.VarChar).Value = book.Sequence;
                    command.Parameters.Add("@seqnumber", FbDbType.VarChar).Value = book.SequenceNumber;
                    command.Parameters.Add("@sequenceid", FbDbType.Integer).Value = book.SequenceId;
                    command.Parameters.Add("@di_progused", FbDbType.VarChar).Value = book.ProgrammUsed;
                    command.Parameters.Add("@di_datevalue", FbDbType.Date).Value = book.DocumentDateValue;
                    command.Parameters.Add("@di_datevisible", FbDbType.VarChar).Value = book.DocumentDateText;
                    command.Parameters.Add("@di_srcurl", FbDbType.VarChar).Value = book.SrcUrl;
                    command.Parameters.Add("@di_srcocr", FbDbType.VarChar).Value = book.SrcOcr;
                    command.Parameters.Add("@oldid", FbDbType.VarChar).Value = book.Id;
                    command.Parameters.Add("@di_version", FbDbType.VarChar).Value = book.Version;
                    command.Parameters.Add("@di_history", FbDbType.Text).Value = book.History;
                    command.Parameters.Add("@pi_bookname", FbDbType.VarChar).Value = book.BookName;
                    command.Parameters.Add("@pi_publisher", FbDbType.VarChar).Value = book.Publisher;
                    command.Parameters.Add("@pi_city", FbDbType.VarChar).Value = book.City;
                    command.Parameters.Add("@pi_year", FbDbType.VarChar).Value = book.Year;
                    command.Parameters.Add("@pi_isbn", FbDbType.VarChar).Value = book.Isbn;
                    command.Parameters.Add("@custominfo", FbDbType.Text).Value = book.CustomInfo;
                    command.Parameters.Add("@datein", FbDbType.TimeStamp).Value = book.DateInserted;
                    command.Parameters.Add("@dateupdated", FbDbType.TimeStamp).Value = book.DateUpdated;
                    command.Parameters.Add("@myid", FbDbType.VarChar).Value = book.SurrogateId;
                    command.Parameters.Add("@ext", FbDbType.VarChar).Value = book.Extension;
                    command.Parameters.Add("@filesize", FbDbType.Float).Value = book.FileSize;
                    command.Parameters.Add("@filename", FbDbType.VarChar).Value = book.FileName;
                    command.Parameters.Add("@filepath", FbDbType.VarChar).Value = book.FilePath;
                    command.Parameters.Add("@filedate", FbDbType.TimeStamp).Value = book.FileDate;
                    command.Parameters.Add("@userid", FbDbType.Integer).Value = book.Userid;
                    command.Parameters.Add("@username", FbDbType.VarChar).Value = book.Username;

                    command.Parameters.Add("@bookid", FbDbType.Integer).Direction = ParameterDirection.Output;

                    command.ExecuteNonQuery();

                    book.BookId = Convert.ToInt32(command.Parameters["@bookid"].Value);
                }

                commandText =
                    "INSERT INTO BOOK_AUTOR (ID, BOOKID, AUTORID) VALUES (GEN_ID(GEN_BOOK_AUTOR_ID, 1), @bookid, @autorid)";

                using (FbCommand command = this.connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;
                    command.CommandText = commandText;
                    command.Transaction = transaction as FbTransaction;

                    command.Parameters.Add("@bookid", FbDbType.Integer);
                    command.Parameters.Add("@autorid", FbDbType.Integer);
                    command.Prepare();

                    foreach (Author bookAuthor in bookAuthors)
                    {
                        command.Parameters["@bookid"].Value = book.BookId;
                        command.Parameters["@autorid"].Value = bookAuthor.Id;

                        command.ExecuteNonQuery();
                    }
                }

                commandText =
                    "INSERT INTO BOOK_DOCAUTOR (ID, BOOKID, DOCAUTORID) VALUES (GEN_ID(GEN_BOOK_DOCAUTOR_ID, 1), @bookid, @autorid)";

                using (FbCommand command = this.connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;
                    command.CommandText = commandText;
                    command.Transaction = transaction as FbTransaction;

                    command.Parameters.Add("@bookid", FbDbType.Integer);
                    command.Parameters.Add("@autorid", FbDbType.Integer);
                    command.Prepare();

                    foreach (Author documentAuthor in documentAuthors)
                    {
                        command.Parameters["@bookid"].Value = book.BookId;
                        command.Parameters["@autorid"].Value = documentAuthor.Id;

                        command.ExecuteNonQuery();
                    }
                }

                commandText =
                    "INSERT INTO BOOK_TRANSLATE (ID, BOOKID, TRANSLATEID) VALUES (GEN_ID(GEN_BOOK_TRANSLATE_ID, 1), @bookid, @autorid)";

                using (FbCommand command = this.connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;
                    command.CommandText = commandText;
                    command.Transaction = transaction as FbTransaction;

                    command.Parameters.Add("@bookid", FbDbType.Integer);
                    command.Parameters.Add("@autorid", FbDbType.Integer);
                    command.Prepare();

                    foreach (Author translator in bookTranslators)
                    {
                        command.Parameters["@bookid"].Value = book.BookId;
                        command.Parameters["@autorid"].Value = translator.Id;

                        command.ExecuteNonQuery();
                    }
                }

                commandText =
                    "INSERT INTO BOOK_SEQUENCE (BOOKID, SEQUENCEID) VALUES (@bookid, @sequenceid)";

                using (FbCommand command = this.connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;
                    command.CommandText = commandText;
                    command.Transaction = transaction as FbTransaction;

                    command.Parameters.Add("@bookid", FbDbType.Integer);
                    command.Parameters.Add("@sequenceid", FbDbType.Integer);
                    command.Prepare();

                    foreach (SequenceInfo sequenceInfo in bookSequences)
                    {
                        command.Parameters["@bookid"].Value = book.BookId;
                        command.Parameters["@sequenceid"].Value = sequenceInfo.SequenceId;

                        command.ExecuteNonQuery();
                    }
                }

                commandText =
                    "INSERT INTO BOOK_GENRE (ID, BOOKID, GENREID) VALUES (GEN_ID(GEN_BOOK_GENRE_ID, 1), @bookid, @genreid)";

                using (FbCommand command = this.connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;
                    command.CommandText = commandText;
                    command.Transaction = transaction as FbTransaction;

                    command.Parameters.Add("@bookid", FbDbType.Integer);
                    command.Parameters.Add("@genreid", FbDbType.Char);
                    command.Prepare();

                    foreach (Genre genre in bookGenres)
                    {
                        command.Parameters["@bookid"].Value = book.BookId;
                        command.Parameters["@genreid"].Value = genre.Name;

                        command.ExecuteNonQuery();
                    }
                }

                return book;
            }
            catch (FbException exp)
            {
                throw new DatabaseException(exp.Message, exp);
            }
            finally
            {
                this.manager.EndConnect();
            }
        }
        public BookInfo CreateFictionBook(FictionBook fictionBook, Stream content, Fb2DocumentEntry documentEntry)
        {
            this.manager.BeginConnect();

            try
            {
                List<Author> authorList = AuthorList;

                FbTransaction transaction = this.connection.BeginTransaction(IsolationLevel.ReadUncommitted);

                try
                {
                    DatabaseInfo info = this.DatabaseInfo;
                    FileNameProvider provider = this.NamingProvider;

                    BookInfo book = CreateFictionBook(fictionBook, content, documentEntry, transaction);

                    if (info.WorkMode == (short)StorageMode.FileSystem)
                    {
                        string bookFilename = GetFilename(fictionBook) + ".fb2";
                        string outputFullPath = Path.Combine(info.MountPoint, bookFilename);
                        string outputDirectory = Path.GetDirectoryName(outputFullPath).Trim();

                        string outputFilename = Path.GetFileNameWithoutExtension(outputFullPath).Trim();
                        outputFilename = FileUtils.GetOutputFileName(outputDirectory, outputFilename, ".fb2.zip");

                        if (!Directory.Exists(outputDirectory))
                        {
                            Directory.CreateDirectory(outputDirectory);
                        }

                        //using (ZipFile file = ZipFile.Create(outputFilename))
                        //{
                        //    file.UseZip64 = UseZip64.Off;

                        //    file.BeginUpdate();
                        //    file.Add(new StreamDataSource(content), Path.GetFileName(bookFilename));
                        //    file.CommitUpdate();
                        //}

                        IOutArchive archive = this.format.CreateOutArchive(SevenZipFormat.GetClassIdFromKnownFormat(KnownSevenZipFormat.Zip));

                        try
                        {
                            using (FileStream stream = File.Open(outputFilename, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
                            {
                                OutStreamWrapper archiveStream = new OutStreamWrapper(stream);
                                archive.UpdateItems(archiveStream, 1, new UpdateBookCallback(content, Path.GetFileName(bookFilename), documentEntry));
                            }
                        }
                        finally
                        {
                            Marshal.ReleaseComObject(archive);
                        }

                        string commandText =
                            " UPDATE BOOK " +
                            "   SET FILENAME = @filename " +
                            "   WHERE BOOKID = @bookid ";

                        using (FbCommand command = this.connection.CreateCommand())
                        {
                            command.CommandType = CommandType.Text;
                            command.CommandText = commandText;
                            command.Transaction = transaction;

                            command.Parameters.Add("@bookid", FbDbType.Integer).Value = book.BookId;
                            command.Parameters.Add("@filename", FbDbType.VarChar).Value = FileUtils.SplitFilePath(info.MountPoint, outputFilename);

                            command.ExecuteNonQuery();
                        }
                    }
                    else if (info.WorkMode == (short)StorageMode.Database)
                    {
                        string bookFilename = GetFilename(fictionBook) + ".fb2";

                        using(MemoryStream stream = new MemoryStream())
                        {
                            //using (ZipFile file = ZipFile.Create(stream))
                            //{
                            //    file.UseZip64 = UseZip64.Off;

                            //    file.BeginUpdate();
                            //    file.Add(new StreamDataSource(content), Path.GetFileName(bookFilename));
                            //    file.CommitUpdate();
                            //}

                            IOutArchive archive = this.format.CreateOutArchive(SevenZipFormat.GetClassIdFromKnownFormat(KnownSevenZipFormat.Zip));

                            try
                            {
                                OutStreamWrapper archiveStream = new OutStreamWrapper(stream);
                                archive.UpdateItems(archiveStream, 1, new UpdateBookCallback(content, bookFilename, documentEntry));
                            }
                            finally
                            {
                                Marshal.ReleaseComObject(archive);
                            }

                            stream.Capacity = (int)stream.Length;

                            string commandText =
                                " UPDATE BOOK " +
                                "   SET FILENAME = @filename, TEXT = @text " +
                                "   WHERE BOOKID = @bookid ";

                            using (FbCommand command = this.connection.CreateCommand())
                            {
                                command.CommandType = CommandType.Text;
                                command.CommandText = commandText;
                                command.Transaction = transaction;

                                command.Parameters.Add("@bookid", FbDbType.Integer).Value = book.BookId;
                                command.Parameters.Add("@text", FbDbType.Binary).Value = stream.GetBuffer();
                                command.Parameters.Add("@filename", FbDbType.VarChar).Value = bookFilename + ".zip";

                                command.ExecuteNonQuery();
                            }
                        }
                    }
                    else if (info.WorkMode == (short)StorageMode.IndexOnly)
                    {
                        string commandText =
                            " UPDATE BOOK " +
                            "   SET FILENAME = @filename " +
                            "   WHERE BOOKID = @bookid ";

                        using (FbCommand command = this.connection.CreateCommand())
                        {
                            command.CommandType = CommandType.Text;
                            command.CommandText = commandText;
                            command.Transaction = transaction;

                            command.Parameters.Add("@bookid", FbDbType.Integer).Value = book.BookId;
                            command.Parameters.Add("@filename", FbDbType.VarChar).Value = documentEntry.Filename;

                            command.ExecuteNonQuery();
                        }
                    }

                    transaction.Commit();
                    return book;
                }
                catch
                {
                    transaction.Rollback();
                    throw;
                }
            }
            finally
            {
                this.manager.EndConnect();
            }
        }
 public UpdateBookCallback(Stream content, string filename, Fb2DocumentEntry documentEntry)
 {
     this.content = content;
     this.filename = filename;
     this.documentEntry = documentEntry;
 }