public BriefBookInfo(BookInfo bookInfo)
        {
            if (bookInfo == null)
            {
                throw new ArgumentNullException("bookInfo");
            }

            this.bookInfo = bookInfo;
        }
        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 UpdateFictionBook(BookInfo bookInfo, FictionBook fictionBook, Stream content, Fb2DocumentEntry documentEntry)
        {
            this.manager.BeginConnect();

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

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

                    BookInfo book = UpdateFictionBook(bookInfo, fictionBook, content, documentEntry, transaction);

                    if (info.WorkMode == (short)StorageMode.FileSystem)
                    {
                        if(!String.IsNullOrEmpty(book.FileName))
                        {
                            string filename = Path.Combine(info.MountPoint, book.FileName);
                            if (File.Exists(filename))
                            {
                                File.Delete(filename);
                            }
                        }

                        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();
            }
        }