/* * Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org> * * 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.book; import java.math.BigDecimal; import java.util.*; import org.fbreader.util.ComparisonUtil; import org.geometerplus.zlibrary.core.util.MiscUtil; import org.geometerplus.zlibrary.core.util.RationalNumber; import org.geometerplus.fbreader.sort.TitledEntity; public abstract class AbstractBook extends TitledEntity<AbstractBook> { public static final String FAVORITE_LABEL = "favorite"; public static final String READ_LABEL = "read"; public static final String SYNCHRONISED_LABEL = "sync-success"; public static final String SYNC_FAILURE_LABEL = "sync-failure"; public static final String SYNC_DELETED_LABEL = "sync-deleted"; public static final String SYNC_TOSYNC_LABEL = "sync-tosync"; protected volatile long myId; protected volatile String myEncoding; protected volatile String myLanguage; protected volatile List<Author> myAuthors; protected volatile List<Tag> myTags; protected volatile List<Label> myLabels; protected volatile SeriesInfo mySeriesInfo; protected volatile List<UID> myUids; protected volatile RationalNumber myProgress; public volatile boolean HasBookmark; protected enum SaveState { Saved, ProgressNotSaved, NotSaved; }; protected volatile SaveState mySaveState = SaveState.NotSaved; AbstractBook(long id, String title, String encoding, String language) { super(title); myId = id; myEncoding = encoding; myLanguage = language; mySaveState = SaveState.Saved; } public abstract String getPath(); public void updateFrom(AbstractBook book) { if (book == null || myId != book.myId) { return; } setTitle(book.getTitle()); setEncoding(book.myEncoding); setLanguage(book.myLanguage); if (!ComparisonUtil.equal(myAuthors, book.myAuthors)) { myAuthors = book.myAuthors != null ? new ArrayList<Author>(book.myAuthors) : null; mySaveState = SaveState.NotSaved; } if (!ComparisonUtil.equal(myTags, book.myTags)) { myTags = book.myTags != null ? new ArrayList<Tag>(book.myTags) : null; mySaveState = SaveState.NotSaved; } if (!MiscUtil.listsEquals(myLabels, book.myLabels)) { myLabels = book.myLabels != null ? new ArrayList<Label>(book.myLabels) : null; mySaveState = SaveState.NotSaved; } if (!ComparisonUtil.equal(mySeriesInfo, book.mySeriesInfo)) { mySeriesInfo = book.mySeriesInfo; mySaveState = SaveState.NotSaved; } if (!MiscUtil.listsEquals(myUids, book.myUids)) { myUids = book.myUids != null ? new ArrayList<UID>(book.myUids) : null; mySaveState = SaveState.NotSaved; } setProgress(book.myProgress); if (HasBookmark != book.HasBookmark) { HasBookmark = book.HasBookmark; mySaveState = SaveState.NotSaved; } } public final List<Author> authors() { return myAuthors != null ? Collections.unmodifiableList(myAuthors) : Collections.<Author>emptyList(); } public final String authorsString(String separator) { final List<Author> authors = myAuthors; if (authors == null || authors.isEmpty()) { return null; } final StringBuilder buffer = new StringBuilder(); boolean first = true; for (Author a : authors) { if (!first) { buffer.append(separator); } buffer.append(a.DisplayName); first = false; } return buffer.toString(); } void addAuthorWithNoCheck(Author author) { if (myAuthors == null) { myAuthors = new ArrayList<Author>(); } myAuthors.add(author); } public void removeAllAuthors() { if (myAuthors != null) { myAuthors = null; mySaveState = SaveState.NotSaved; } } public void addAuthor(Author author) { if (author == null) { return; } if (myAuthors == null) { myAuthors = new ArrayList<Author>(); myAuthors.add(author); mySaveState = SaveState.NotSaved; } else if (!myAuthors.contains(author)) { myAuthors.add(author); mySaveState = SaveState.NotSaved; } } public void addAuthor(String name) { addAuthor(name, null); } public void addAuthor(String name, String sortKey) { addAuthor(Author.create(name, sortKey)); } public long getId() { return myId; } @Override public void setTitle(String title) { if (title == null) { return; } title = title.trim(); if (title.length() == 0) { return; } if (!getTitle().equals(title)) { super.setTitle(title); mySaveState = SaveState.NotSaved; } } public SeriesInfo getSeriesInfo() { return mySeriesInfo; } void setSeriesInfoWithNoCheck(String name, String index) { mySeriesInfo = SeriesInfo.createSeriesInfo(name, index); } public void setSeriesInfo(String name, String index) { setSeriesInfo(name, SeriesInfo.createIndex(index)); } public void setSeriesInfo(String name, BigDecimal index) { if (mySeriesInfo == null) { if (name != null) { mySeriesInfo = new SeriesInfo(name, index); mySaveState = SaveState.NotSaved; } } else if (name == null) { mySeriesInfo = null; mySaveState = SaveState.NotSaved; } else if (!name.equals(mySeriesInfo.Series.getTitle()) || mySeriesInfo.Index != index) { mySeriesInfo = new SeriesInfo(name, index); mySaveState = SaveState.NotSaved; } } @Override public String getLanguage() { return myLanguage; } public void setLanguage(String language) { if (!ComparisonUtil.equal(myLanguage, language)) { myLanguage = language; resetSortKey(); mySaveState = SaveState.NotSaved; } } public String getEncodingNoDetection() { return myEncoding; } public void setEncoding(String encoding) { if (!ComparisonUtil.equal(myEncoding, encoding)) { myEncoding = encoding; mySaveState = SaveState.NotSaved; } } public List<Tag> tags() { return myTags != null ? Collections.unmodifiableList(myTags) : Collections.<Tag>emptyList(); } public final String tagsString(String separator) { final List<Tag> tags = myTags; if (tags == null || tags.isEmpty()) { return null; } final HashSet<String> tagNames = new HashSet<String>(); final StringBuilder buffer = new StringBuilder(); boolean first = true; for (Tag t : tags) { if (!first) { buffer.append(separator); } if (!tagNames.contains(t.Name)) { tagNames.add(t.Name); buffer.append(t.Name); first = false; } } return buffer.toString(); } void addTagWithNoCheck(Tag tag) { if (myTags == null) { myTags = new ArrayList<Tag>(); } myTags.add(tag); } public void removeAllTags() { if (myTags != null) { myTags = null; mySaveState = SaveState.NotSaved; } } public void addTag(Tag tag) { if (tag != null) { if (myTags == null) { myTags = new ArrayList<Tag>(); } if (!myTags.contains(tag)) { myTags.add(tag); mySaveState = SaveState.NotSaved; } } } public void addTag(String tagName) { addTag(Tag.getTag(null, tagName)); } public boolean hasLabel(String name) { for (Label l : labels()) { if (name.equals(l.Name)) { return true; } } return false; } public List<Label> labels() { return myLabels != null ? Collections.unmodifiableList(myLabels) : Collections.<Label>emptyList(); } void addLabelWithNoCheck(Label label) { if (myLabels == null) { myLabels = new ArrayList<Label>(); } myLabels.add(label); } public void addNewLabel(String label) { addLabel(new Label(label)); } public void addLabel(Label label) { if (myLabels == null) { myLabels = new ArrayList<Label>(); } if (!myLabels.contains(label)) { myLabels.add(label); mySaveState = SaveState.NotSaved; } } public void removeLabel(String label) { if (myLabels != null && myLabels.remove(new Label(label))) { mySaveState = SaveState.NotSaved; } } public List<UID> uids() { return myUids != null ? Collections.unmodifiableList(myUids) : Collections.<UID>emptyList(); } public void addUid(String type, String id) { addUid(new UID(type, id)); } void addUidWithNoCheck(UID uid) { if (uid == null) { return; } if (myUids == null) { myUids = new ArrayList<UID>(); } myUids.add(uid); } public void addUid(UID uid) { if (uid == null) { return; } if (myUids == null) { myUids = new ArrayList<UID>(); } if (!myUids.contains(uid)) { myUids.add(uid); mySaveState = SaveState.NotSaved; } } public boolean matchesUid(UID uid) { return myUids.contains(uid); } public RationalNumber getProgress() { return myProgress; } public void setProgress(RationalNumber progress) { if (!ComparisonUtil.equal(myProgress, progress)) { myProgress = progress; if (mySaveState == SaveState.Saved) { mySaveState = SaveState.ProgressNotSaved; } } } public void setProgressWithNoCheck(RationalNumber progress) { myProgress = progress; } public boolean matches(String pattern) { if (MiscUtil.matchesIgnoreCase(getTitle(), pattern)) { return true; } if (mySeriesInfo != null && MiscUtil.matchesIgnoreCase(mySeriesInfo.Series.getTitle(), pattern)) { return true; } if (myAuthors != null) { for (Author author : myAuthors) { if (MiscUtil.matchesIgnoreCase(author.DisplayName, pattern)) { return true; } } } if (myTags != null) { for (Tag tag : myTags) { if (MiscUtil.matchesIgnoreCase(tag.Name, pattern)) { return true; } } } String fileName = getPath(); // first archive delimiter int index = fileName.indexOf(":"); // last path delimiter before first archive delimiter if (index == -1) { index = fileName.lastIndexOf("/"); } else { index = fileName.lastIndexOf("/", index); } fileName = fileName.substring(index + 1); if (MiscUtil.matchesIgnoreCase(fileName, pattern)) { return true; } return false; } @Override public String toString() { return getClass().getName() + "[" + getPath() + ", " + myId + ", " + getTitle() + "]"; } }