1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-04 10:19:33 +02:00
FBReaderJ/src/org/geometerplus/fbreader/library/Book.java
2012-04-13 19:02:26 +02:00

532 lines
13 KiB
Java

/*
* Copyright (C) 2007-2012 Geometer Plus <contact@geometerplus.com>
*
* 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.
*/
package org.geometerplus.fbreader.library;
import java.lang.ref.WeakReference;
import java.util.*;
import java.io.InputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.geometerplus.zlibrary.core.util.ZLMiscUtil;
import org.geometerplus.zlibrary.core.filesystem.*;
import org.geometerplus.zlibrary.core.image.ZLImage;
import org.geometerplus.zlibrary.text.view.ZLTextPosition;
import org.geometerplus.fbreader.formats.*;
import org.geometerplus.fbreader.bookmodel.BookReadingException;
import org.geometerplus.fbreader.Paths;
public class Book {
public static Book getById(long bookId) {
final Book book = BooksDatabase.Instance().loadBook(bookId);
if (book == null) {
return null;
}
book.loadLists();
final ZLFile bookFile = book.File;
final ZLPhysicalFile physicalFile = bookFile.getPhysicalFile();
if (physicalFile == null) {
return book;
}
if (!physicalFile.exists()) {
return null;
}
FileInfoSet fileInfos = new FileInfoSet(physicalFile);
if (fileInfos.check(physicalFile, physicalFile != bookFile)) {
return book;
}
fileInfos.save();
try {
book.readMetaInfo();
return book;
} catch (BookReadingException e) {
return null;
}
}
public static Book getByFile(ZLFile bookFile) {
if (bookFile == null) {
return null;
}
final ZLPhysicalFile physicalFile = bookFile.getPhysicalFile();
if (physicalFile != null && !physicalFile.exists()) {
return null;
}
final FileInfoSet fileInfos = new FileInfoSet(bookFile);
Book book = BooksDatabase.Instance().loadBookByFile(fileInfos.getId(bookFile), bookFile);
if (book != null) {
book.loadLists();
}
if (book != null && fileInfos.check(physicalFile, physicalFile != bookFile)) {
return book;
}
fileInfos.save();
try {
if (book == null) {
book = new Book(bookFile);
} else {
book.readMetaInfo();
}
} catch (BookReadingException e) {
return null;
}
book.save();
return book;
}
public final ZLFile File;
private volatile long myId;
private volatile String myEncoding;
private volatile String myLanguage;
private volatile String myTitle;
private volatile List<Author> myAuthors;
private volatile List<Tag> myTags;
private volatile SeriesInfo mySeriesInfo;
private volatile boolean myIsSaved;
private static final WeakReference<ZLImage> NULL_IMAGE = new WeakReference<ZLImage>(null);
private WeakReference<ZLImage> myCover;
Book(long id, ZLFile file, String title, String encoding, String language) {
myId = id;
File = file;
myTitle = title;
myEncoding = encoding;
myLanguage = language;
myIsSaved = true;
}
Book(ZLFile file) throws BookReadingException {
myId = -1;
File = file;
readMetaInfo();
}
public void reloadInfoFromFile() {
try {
readMetaInfo();
save();
} catch (BookReadingException e) {
// ignore
}
}
public void reloadInfoFromDatabase() {
final BooksDatabase database = BooksDatabase.Instance();
database.reloadBook(this);
myAuthors = database.loadAuthors(myId);
myTags = database.loadTags(myId);
mySeriesInfo = database.loadSeriesInfo(myId);
myIsSaved = true;
}
public FormatPlugin getPlugin() throws BookReadingException {
final FormatPlugin plugin = PluginCollection.Instance().getPlugin(File);
if (plugin == null) {
throw new BookReadingException("pluginNotFound", File);
}
return plugin;
}
void readMetaInfo() throws BookReadingException {
readMetaInfo(getPlugin());
}
private void readMetaInfo(FormatPlugin plugin) throws BookReadingException {
myEncoding = null;
myLanguage = null;
myTitle = null;
myAuthors = null;
myTags = null;
mySeriesInfo = null;
myIsSaved = false;
plugin.readMetaInfo(this);
if (myTitle == null || myTitle.length() == 0) {
final String fileName = File.getShortName();
final int index = fileName.lastIndexOf('.');
setTitle(index > 0 ? fileName.substring(0, index) : fileName);
}
final String demoPathPrefix = Paths.BooksDirectoryOption().getValue() + java.io.File.separator + "Demos" + java.io.File.separator;
if (File.getPath().startsWith(demoPathPrefix)) {
final String demoTag = LibraryUtil.resource().getResource("demo").getValue();
setTitle(getTitle() + " (" + demoTag + ")");
addTag(demoTag);
}
}
private void loadLists() {
final BooksDatabase database = BooksDatabase.Instance();
myAuthors = database.loadAuthors(myId);
myTags = database.loadTags(myId);
mySeriesInfo = database.loadSeriesInfo(myId);
myIsSaved = true;
}
public List<Author> authors() {
return (myAuthors != null) ? Collections.unmodifiableList(myAuthors) : Collections.<Author>emptyList();
}
void addAuthorWithNoCheck(Author author) {
if (myAuthors == null) {
myAuthors = new ArrayList<Author>();
}
myAuthors.add(author);
}
private void addAuthor(Author author) {
if (author == null) {
return;
}
if (myAuthors == null) {
myAuthors = new ArrayList<Author>();
myAuthors.add(author);
myIsSaved = false;
} else if (!myAuthors.contains(author)) {
myAuthors.add(author);
myIsSaved = false;
}
}
public void addAuthor(String name) {
addAuthor(name, "");
}
public void addAuthor(String name, String sortKey) {
String strippedName = name;
strippedName.trim();
if (strippedName.length() == 0) {
return;
}
String strippedKey = sortKey;
strippedKey.trim();
if (strippedKey.length() == 0) {
int index = strippedName.lastIndexOf(' ');
if (index == -1) {
strippedKey = strippedName;
} else {
strippedKey = strippedName.substring(index + 1);
while ((index >= 0) && (strippedName.charAt(index) == ' ')) {
--index;
}
strippedName = strippedName.substring(0, index + 1) + ' ' + strippedKey;
}
}
addAuthor(new Author(strippedName, strippedKey));
}
public long getId() {
return myId;
}
public String getTitle() {
return myTitle;
}
public void setTitle(String title) {
if (!ZLMiscUtil.equals(myTitle, title)) {
myTitle = title;
myIsSaved = false;
}
}
public SeriesInfo getSeriesInfo() {
return mySeriesInfo;
}
void setSeriesInfoWithNoCheck(String name, float index) {
mySeriesInfo = new SeriesInfo(name, index);
}
public void setSeriesInfo(String name, float index) {
if (mySeriesInfo == null) {
if (name != null) {
mySeriesInfo = new SeriesInfo(name, index);
myIsSaved = false;
}
} else if (name == null) {
mySeriesInfo = null;
myIsSaved = false;
} else if (!name.equals(mySeriesInfo.Name) || mySeriesInfo.Index != index) {
mySeriesInfo = new SeriesInfo(name, index);
myIsSaved = false;
}
}
public String getLanguage() {
return myLanguage;
}
public void setLanguage(String language) {
if (!ZLMiscUtil.equals(myLanguage, language)) {
myLanguage = language;
myIsSaved = false;
}
}
public String getEncoding() {
if (myEncoding == null) {
try {
getPlugin().detectLanguageAndEncoding(this);
} catch (BookReadingException e) {
}
if (myEncoding == null) {
setEncoding("utf-8");
}
}
return myEncoding;
}
public String getEncodingNoDetection() {
return myEncoding;
}
public void setEncoding(String encoding) {
if (!ZLMiscUtil.equals(myEncoding, encoding)) {
myEncoding = encoding;
myIsSaved = false;
}
}
public List<Tag> tags() {
return (myTags != null) ? Collections.unmodifiableList(myTags) : Collections.<Tag>emptyList();
}
void addTagWithNoCheck(Tag tag) {
if (myTags == null) {
myTags = new ArrayList<Tag>();
}
myTags.add(tag);
}
public void addTag(Tag tag) {
if (tag != null) {
if (myTags == null) {
myTags = new ArrayList<Tag>();
}
if (!myTags.contains(tag)) {
myTags.add(tag);
myIsSaved = false;
}
}
}
public void addTag(String tagName) {
addTag(Tag.getTag(null, tagName));
}
boolean matches(String pattern) {
if (myTitle != null && ZLMiscUtil.matchesIgnoreCase(myTitle, pattern)) {
return true;
}
if (mySeriesInfo != null && ZLMiscUtil.matchesIgnoreCase(mySeriesInfo.Name, pattern)) {
return true;
}
if (myAuthors != null) {
for (Author author : myAuthors) {
if (ZLMiscUtil.matchesIgnoreCase(author.DisplayName, pattern)) {
return true;
}
}
}
if (myTags != null) {
for (Tag tag : myTags) {
if (ZLMiscUtil.matchesIgnoreCase(tag.Name, pattern)) {
return true;
}
}
}
if (ZLMiscUtil.matchesIgnoreCase(File.getLongName(), pattern)) {
return true;
}
return false;
}
public boolean save() {
if (myIsSaved) {
return false;
}
final BooksDatabase database = BooksDatabase.Instance();
database.executeAsATransaction(new Runnable() {
public void run() {
if (myId >= 0) {
final FileInfoSet fileInfos = new FileInfoSet(File);
database.updateBookInfo(myId, fileInfos.getId(File), myEncoding, myLanguage, myTitle);
} else {
myId = database.insertBookInfo(File, myEncoding, myLanguage, myTitle);
storeAllVisitedHyperinks();
}
long index = 0;
database.deleteAllBookAuthors(myId);
for (Author author : authors()) {
database.saveBookAuthorInfo(myId, index++, author);
}
database.deleteAllBookTags(myId);
for (Tag tag : tags()) {
database.saveBookTagInfo(myId, tag);
}
database.saveBookSeriesInfo(myId, mySeriesInfo);
}
});
myIsSaved = true;
return true;
}
public ZLTextPosition getStoredPosition() {
return BooksDatabase.Instance().getStoredPosition(myId);
}
public void storePosition(ZLTextPosition position) {
if (myId != -1) {
BooksDatabase.Instance().storePosition(myId, position);
}
}
private Set<String> myVisitedHyperlinks;
private void initHyperlinkSet() {
if (myVisitedHyperlinks == null) {
myVisitedHyperlinks = new TreeSet<String>();
if (myId != -1) {
myVisitedHyperlinks.addAll(BooksDatabase.Instance().loadVisitedHyperlinks(myId));
}
}
}
public boolean isHyperlinkVisited(String linkId) {
initHyperlinkSet();
return myVisitedHyperlinks.contains(linkId);
}
public void markHyperlinkAsVisited(String linkId) {
initHyperlinkSet();
if (!myVisitedHyperlinks.contains(linkId)) {
myVisitedHyperlinks.add(linkId);
if (myId != -1) {
BooksDatabase.Instance().addVisitedHyperlink(myId, linkId);
}
}
}
private void storeAllVisitedHyperinks() {
if (myId != -1 && myVisitedHyperlinks != null) {
for (String linkId : myVisitedHyperlinks) {
BooksDatabase.Instance().addVisitedHyperlink(myId, linkId);
}
}
}
public void insertIntoBookList() {
if (myId != -1) {
BooksDatabase.Instance().insertIntoBookList(myId);
}
}
public String getContentHashCode() {
InputStream stream = null;
try {
final MessageDigest hash = MessageDigest.getInstance("SHA-256");
stream = File.getInputStream();
final byte[] buffer = new byte[2048];
while (true) {
final int nread = stream.read(buffer);
if (nread == -1) {
break;
}
hash.update(buffer, 0, nread);
}
final Formatter f = new Formatter();
for (byte b : hash.digest()) {
f.format("%02X", b & 0xFF);
}
return f.toString();
} catch (IOException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
}
synchronized ZLImage getCover() {
if (myCover == NULL_IMAGE) {
return null;
} else if (myCover != null) {
final ZLImage image = myCover.get();
if (image != null) {
return image;
}
}
ZLImage image = null;
try {
image = getPlugin().readCover(File);
} catch (BookReadingException e) {
// ignore
}
myCover = image != null ? new WeakReference<ZLImage>(image) : NULL_IMAGE;
return image;
}
@Override
public int hashCode() {
return (int)myId;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Book)) {
return false;
}
return File.equals(((Book)o).File);
}
}