package com.gmail.dpierron.calibre.datamodel;
/**
* Class that supports the data model that is used within Calibre2opds
*
* The data model is largely determined by the Calibre database structure.
*
* NOTE: There should only ever be one instance of this object, so all
* global variables and methods are declared static
*/
import com.gmail.dpierron.calibre.database.Database;
import com.gmail.dpierron.calibre.datamodel.filter.BookFilter;
import com.gmail.dpierron.tools.Composite;
import com.gmail.dpierron.tools.Helper;
import com.gmail.dpierron.tools.i18n.Localization;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.text.Normalizer;
import java.util.*;
import java.util.regex.Pattern;
public class DataModel {
private final static Logger logger = LogManager.getLogger(DataModel.class);
protected static final String IMPLICIT_LANGUAGE_TAG_PREFIX = "Lang:";
private static Map<String, List<EBookFile>> mapOfEBookFilesByBookId;
private static Map<String, List<Publisher>> mapOfPublishersByBookId;
private static Map<String, List<Author>> mapOfAuthorsByBookId;
private static Map<String, List<Tag>> mapOfTagsByBookId;
private static Map<String, List<Series>> mapOfSeriesByBookId;
private static Map<String, List<String>> mapOfCommentsByBookId;
// private static Map<String, List<Tag>> mapOfCustomTagsByBookId;
// private static Map<String, List<Series>> mapOfCustomSeriesByBookId;
private static List<Book> listOfBooks;
private static Map<String, Book> mapOfBooks;
private static List<Tag> listOfTags;
private static Map<String, Tag> mapOfTags;
private static Map<Tag, List<Book>> mapOfBooksByTag;
// private static List<Tag> listOfCustomTags;
private static List<Author> listOfAuthors;
private static Map<String, Author> mapOfAuthors;
private static Map<Author, List<Book>> mapOfBooksByAuthor;
private static List<Series> listOfSeries;
private static Map<String, Series> mapOfSeries;
private static Map<Series, List<Book>> mapOfBooksBySeries;
// private static List<Series> listOfCustomSeries;
private static List<BookRating> listOfRatings;
private static Map<String, BookRating> mapOfRatings;
private static Map<BookRating, List<Book>> mapOfBooksByRating;
private static List<Publisher> listOfPublishers;
private static Map<String, Publisher> mapOfPublishers;
private static Map<Publisher, List<Book>> mapOfBooksByPublisher;
private static Map<String, Language> mapOfLanguagesById;
private static Map<String, Language> mapOfLanguagesByIsoCode;
private static Map<String, String> mapOfSavedSearches;
private static List<CustomColumnType> listOfCustomColumnTypes;
// private static List<CustomColumnType> listOfCustomColumnTypesReferenced;
private static Map<String, List <CustomColumnValue>> mapOfCustomColumnValuesByBookId;
private static Map<Locale, NoiseWord> mapOfNoisewords;
private static boolean useLanguagesAsTags = true;
private static boolean librarySortAuthor = true;
private static boolean librarySortTitle = true;
private static boolean librarySortSeries = true;
public static void reset() {
mapOfEBookFilesByBookId = null;
mapOfPublishersByBookId = null;
mapOfAuthorsByBookId = null;
mapOfTagsByBookId = null;
mapOfSeriesByBookId = null;
mapOfCommentsByBookId = null;
listOfBooks = null;
mapOfBooks = null;
listOfTags = null;
mapOfTags = null;
mapOfBooksByTag = null;
listOfAuthors = null;
mapOfAuthors = null;
mapOfBooksByAuthor = null;
listOfSeries = null;
mapOfSeries = null;
mapOfBooksBySeries = null;
listOfRatings = null;
mapOfRatings = null;
mapOfBooksByRating = null;
listOfPublishers = null;
mapOfPublishers = null;
mapOfBooksByPublisher = null;
mapOfLanguagesById = null;
mapOfLanguagesByIsoCode = null;
mapOfSavedSearches = null;
listOfCustomColumnTypes = null;
// listOfCustomColumnTypesReferenced = null;
mapOfCustomColumnValuesByBookId = null;
// listOfCustomTags = null;
// listOfCustomSeries = null;
// reset the database
Database.reset();
}
/**
* The following loads up data in a number of different views
* to help with more efficient access to it later in the run.
*
* Some values that are optional are only loaded on demand when
* an attempt is amde to access their data set.
*/
public static void preloadDataModel() {
// Load reference data from database
getMapOfLanguagesById();
getListOfCustomColumnTypes();
// listOfCustomColumnTypesReferenced = listOfCustomColumnTypes; // Initialise to full list
// Build up cross-reference lookups by bookid
getMapOfEBookFilesByBookId();
getMapOfAuthorsByBookId();
getMapOfTagsByBookId();
getMapOfSeriesByBookId();
getMapOfCommentsByBookId();
getListOfTags();
getListOfAuthors();
getListOfSeries();
getListOfBooks();
// Build simple lookups for main data objects by id
// (these will always be needed so get it donw ASAP)
getMapOfBooks();
getMapOfTags();
getMapOfAuthors();
getMapOfSeries();
// Build up cross-reference list by other types
generateImplicitLanguageTags(); // NOTE: Must be done befoe generate BooksByTag listing
getMapOfBooksByTag();
getMapOfBooksByAuthor();
getMapOfBooksBySeries();
getMapOfBooksByRating();
}
/**
* This list should not be very large so we do not mind loading all of it every time
* @return
*/
public static List<CustomColumnType> getListOfCustomColumnTypes () {
if (listOfCustomColumnTypes == null) {
listOfCustomColumnTypes = Database.getlistOfCustoColumnTypes();
}
return listOfCustomColumnTypes;
}
/**
* Get list of Custom Column Values
*
* Since it highly likely that not all custom columns will be referenced,
* the loading should be delayed until we know which ones are required for
* this parituclar run to both improve performance and reduce RAM usage.
*
* @return
*/
public static Map<String, List<CustomColumnValue>> getMapOfCustomColumnValuesByBookId() {
if (mapOfCustomColumnValuesByBookId == null) {
mapOfCustomColumnValuesByBookId = Database.getMapofCustomColumnValuesbyBookId(getListOfCustomColumnTypes());
}
return mapOfCustomColumnValuesByBookId;
}
public static Map<String, String> getMapOfSavedSearches() {
if (mapOfSavedSearches == null) {
mapOfSavedSearches = Database.getMapOfSavedSearches();
}
return mapOfSavedSearches;
}
public static Map<String, Language> getMapOfLanguagesById() {
if (mapOfLanguagesById == null) {
Composite<Map<String, Language>, Map<String, Language>> result = Database.getMapsOfLanguages();
mapOfLanguagesById = result.getFirstElement();
mapOfLanguagesByIsoCode = result.getSecondElement();
}
return mapOfLanguagesById;
}
public static Map<String, Language> getMapOfLanguagesByIsoCode() {
if (mapOfLanguagesByIsoCode == null) {
getMapOfLanguagesById();
}
return mapOfLanguagesById;
}
/**
* Get the list of formats that should exist for eah book
*
* @return
*/
public static Map<String, List<EBookFile>> getMapOfEBookFilesByBookId() {
if (mapOfEBookFilesByBookId == null) {
mapOfEBookFilesByBookId = Database.getMapOfEBookFilesByBookId();
}
return mapOfEBookFilesByBookId;
}
/**
*
* @return
*/
public static Map<String, List<Author>> getMapOfAuthorsByBookId() {
if (mapOfAuthorsByBookId == null) {
mapOfAuthorsByBookId = Database.getMapOfAuthorsByBookId();
}
return mapOfAuthorsByBookId;
}
/**
*
* @return
*/
public static Map<String, List<Publisher>> getMapOfPublishersByBookId() {
if (mapOfPublishersByBookId == null) {
mapOfPublishersByBookId = Database.listPublishersByBookId();
}
return mapOfPublishersByBookId;
}
/**
*
* @return
*/
public static Map<String, List<Tag>> getMapOfTagsByBookId() {
if (mapOfTagsByBookId == null) {
mapOfTagsByBookId = Database.getMapOfTagsByBookId();
}
return mapOfTagsByBookId;
}
/**
*
* @return
*/
public static Map<String, List<Series>> getMapOfSeriesByBookId() {
if (mapOfSeriesByBookId == null) {
mapOfSeriesByBookId = Database.getMapOfSeriesByBookId();
}
return mapOfSeriesByBookId;
}
/**
*
* @return
*/
public static Map<String, List<String>> getMapOfCommentsByBookId() {
if (mapOfCommentsByBookId == null) {
mapOfCommentsByBookId = Database.getMapOfCommentsByBookId();
}
return mapOfCommentsByBookId;
}
/**
*
* @return
*/
public static List<Book> getListOfBooks() {
if (listOfBooks == null) {
listOfBooks = Database.listBooks();
}
return listOfBooks;
}
public static Map<String, Book> getMapOfBooks() {
if (mapOfBooks == null) {
mapOfBooks = new HashMap<String, Book>();
for (Book book : getListOfBooks()) {
mapOfBooks.put(book.getId(), book);
}
}
return mapOfBooks;
}
public static List<Tag> getListOfTags() {
if (listOfTags == null) {
listOfTags = Database.listTags();
}
return listOfTags;
}
/**
* Build up a map of tags by their tag id
*
* @return
*/
public static Map<String, Tag> getMapOfTags() {
if (mapOfTags == null) {
mapOfTags = new HashMap<String, Tag>();
for (Tag tag : getListOfTags()) {
mapOfTags.put(tag.getId(), tag);
}
}
return mapOfTags;
}
/**
* Add a tag to the list of tags
*
* @param tag
*/
public static void addTag(Tag tag) {
if (getListOfTags().contains(tag)) {
return;
}
getListOfTags().add(tag);
getMapOfTags().put(tag.getId(), tag);
}
/**
* Get the mapping of tags to Book Id.
* @return
*/
public static Map<Tag, List<Book>> getMapOfBooksByTag() {
if (mapOfBooksByTag == null) {
mapOfBooksByTag = new HashMap<Tag, List<Book>>();
for (Book book : getListOfBooks()) {
for (Tag tag : book.getTags()) {
List<Book> books = mapOfBooksByTag.get(tag);
if (books == null) {
books = new LinkedList<Book>();
mapOfBooksByTag.put(tag, books);
}
books.add(book);
}
}
}
return mapOfBooksByTag;
}
public static List<Author> getListOfAuthors() {
if (listOfAuthors == null) {
listOfAuthors = Database.listAuthors();
}
return listOfAuthors;
}
public static Map<String, Author> getMapOfAuthors() {
if (mapOfAuthors == null) {
mapOfAuthors = new HashMap<String, Author>();
for (Author author : getListOfAuthors()) {
mapOfAuthors.put(author.getId(), author);
}
}
return mapOfAuthors;
}
public static Map<Author, List<Book>> getMapOfBooksByAuthor() {
if (mapOfBooksByAuthor == null) {
mapOfBooksByAuthor = new HashMap<Author, List<Book>>();
for (Book book : getListOfBooks()) {
for (Author author : book.getAuthors()) {
List<Book> books = mapOfBooksByAuthor.get(author);
if (books == null) {
books = new LinkedList<Book>();
mapOfBooksByAuthor.put(author, books);
}
books.add(book);
}
}
}
return mapOfBooksByAuthor;
}
public static List<Series> getListOfSeries() {
if (listOfSeries == null) {
listOfSeries = Database.listSeries();
}
return listOfSeries;
}
public static Map<String, Series> getMapOfSeries() {
if (mapOfSeries == null) {
mapOfSeries = new HashMap<String, Series>();
for (Series serie : getListOfSeries()) {
mapOfSeries.put(serie.getId(), serie);
}
}
return mapOfSeries;
}
public static Map<Series, List<Book>> getMapOfBooksBySeries() {
if (mapOfBooksBySeries == null) {
mapOfBooksBySeries = new HashMap<Series, List<Book>>();
for (Book book : getListOfBooks()) {
List<Book> books = mapOfBooksBySeries.get(book.getSeries());
if (books == null) {
books = new LinkedList<Book>();
Series series = book.getSeries();
if (series != null)
mapOfBooksBySeries.put(series, books);
}
books.add(book);
}
}
return mapOfBooksBySeries;
}
public static Map<BookRating, List<Book>> getMapOfBooksByRating() {
if (mapOfBooksByRating == null) {
mapOfBooksByRating = new HashMap<BookRating, List<Book>>();
for (Book book : getListOfBooks()) {
List<Book> books = mapOfBooksByRating.get(book.getRating());
if (books == null) {
books = new LinkedList<Book>();
BookRating rating = book.getRating();
if (rating != null)
mapOfBooksByRating.put(rating, books);
}
books.add(book);
}
}
return mapOfBooksByRating;
}
public static List<Publisher> getListOfPublishers() {
if (listOfPublishers == null) {
listOfPublishers = Database.listPublishers();
}
return listOfPublishers;
}
public static Map<String, Publisher> getMapOfPublishers() {
if (mapOfPublishers == null) {
mapOfPublishers = new HashMap<String, Publisher>();
for (Publisher publisher : getListOfPublishers()) {
mapOfPublishers.put(publisher.getId(), publisher);
}
}
return mapOfPublishers;
}
public static Map<Publisher, List<Book>> getMapOfBooksByPublisher() {
if (mapOfBooksByPublisher == null) {
mapOfBooksByPublisher = new HashMap<Publisher, List<Book>>();
for (Book book : getListOfBooks()) {
Publisher publisher = book.getPublisher();
List<Book> books = mapOfBooksByPublisher.get(publisher);
if (books == null) {
books = new LinkedList<Book>();
mapOfBooksByPublisher.put(publisher, books);
}
books.add(book);
}
}
return mapOfBooksByPublisher;
}
public static Map<String, List<GenericDataObject>> splitObjectsByLetter(List<GenericDataObject> objs) {
Comparator<GenericDataObject> comparator = new Comparator<GenericDataObject>() {
public int compare(GenericDataObject o1, GenericDataObject o2) {
String title1 = (o1 == null ? "" : o1.getTitleToSplitByLetter());
String title2 = (o2 == null ? "" : o2.getTitleToSplitByLetter());
return title1.compareTo(title2);
}
};
return splitByLetter(objs, comparator);
}
public static Map<String, List<Book>> splitBooksByLetter(List<Book> books) {
Comparator<Book> comparator = new Comparator<Book>() {
public int compare(Book o1, Book o2) {
String title1 = (o1 == null ? "" : o1.getTitleToSplitByLetter());
String title2 = (o2 == null ? "" : o2.getTitleToSplitByLetter());
return title1.compareTo(title2);
}
};
return splitByLetter(books, comparator);
}
public static Map<String, List<Author>> splitAuthorsByLetter(List<Author> authors) {
Comparator<Author> comparator = new Comparator<Author>() {
public int compare(Author o1, Author o2) {
String author1 = (o1 == null ? "" : o1.getTitleToSplitByLetter());
String author2 = (o2 == null ? "" : o2.getTitleToSplitByLetter());
return author1.compareTo(author2);
}
};
return splitByLetter(authors, comparator);
}
public static Map<String, List<Series>> splitSeriesByLetter(List<Series> series) {
Comparator<Series> comparator = new Comparator<Series>() {
public int compare(Series o1, Series o2) {
String series1 = (o1 == null ? "" : o1.getTitleToSplitByLetter());
String series2 = (o2 == null ? "" : o2.getTitleToSplitByLetter());
return series1.compareTo(series2);
}
};
return splitByLetter(series, comparator);
}
public static Map<String, List<Tag>> splitTagsByLetter(List<Tag> tags) {
Comparator<Tag> comparator = new Comparator<Tag>() {
public int compare(Tag o1, Tag o2) {
String tag1 = (o1 == null ? "" : o1.getName());
String tag2 = (o2 == null ? "" : o2.getName());
return tag1.compareTo(tag2);
}
};
return splitByLetter(tags, comparator);
}
public static Map<DateRange, List<Book>> splitBooksByDate(List<Book> books) {
Map<DateRange, List<Book>> splitByDate = new HashMap<DateRange, List<Book>>();
for (Book book : books) {
DateRange range = DateRange.valueOf((book).getTimestamp());
List<Book> list = splitByDate.get(range);
if (list == null) {
list = new LinkedList<Book>();
splitByDate.put(range, list);
}
list.add(book);
}
return splitByDate;
}
public static Map<DateRange, List<GenericDataObject>> splitObjectsByDate(List<? extends GenericDataObject> objs) {
Map<DateRange, List<GenericDataObject>> splitByDate = new HashMap<DateRange, List<GenericDataObject>>();
for (GenericDataObject obj : objs) {
DateRange range = DateRange.valueOf(((Book)obj).getTimestamp());
List<GenericDataObject> list = splitByDate.get(range);
if (list == null) {
list = new LinkedList<GenericDataObject>();
splitByDate.put(range, list);
}
list.add(obj);
}
return splitByDate;
}
/**
* Split a list of items by the first letter that is different in some (e.g. Mortal, More and Morris will be splitted on t, e and r)
* @param objects
* @param comparator
* @return
*/
public static <T extends SplitableByLetter> Map<String, List<T>> splitByLetter(List<T> objects, Comparator<T> comparator) {
Map<String, List<T>> splitMap = new HashMap<String, List<T>>();
// construct a list of all strings to split
Vector<String> stringsToSplit = new Vector<String>(objects.size());
String commonPart = null;
for (T object : objects) {
if (object == null)
continue;
String string = object.getTitleToSplitByLetter();
// remove any diacritic mark
String temp = Normalizer.normalize(string, Normalizer.Form.NFD);
Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
String norm = pattern.matcher(temp).replaceAll("");
string = norm;
stringsToSplit.add(string);
// find the common part
if (commonPart == null) {
commonPart = string.toUpperCase();
} else if (commonPart.length() > 0) {
String tempCommonPart = commonPart;
while (tempCommonPart.length() > 0 && !string.toUpperCase().startsWith(tempCommonPart)) {
tempCommonPart = tempCommonPart.substring(0, tempCommonPart.length()-1);
}
commonPart = tempCommonPart.toUpperCase();
}
}
// note the position of the first different char
int firstDifferentCharPosition = commonPart.length();
// browse all objects and split them up
for (int i=0; i<objects.size(); i++) {
T object = objects.get(i);
if (object == null)
continue;
String string = stringsToSplit.get(i);
String discriminantPart = "_";
if (Helper.isNotNullOrEmpty(string)) {
if (firstDifferentCharPosition + 1 >= string.length())
discriminantPart = string.toUpperCase();
else
discriminantPart = string.substring(0, firstDifferentCharPosition+1).toUpperCase();
// find the already existing list (of items split by this discriminant part)
List<T> list = splitMap.get(discriminantPart);
if (list == null) {
// no list yet, create one
list = new LinkedList<T>();
splitMap.put(discriminantPart, list);
}
// add the current item to the list
list.add(object);
}
}
// sort each list
for (String letter : splitMap.keySet()) {
List<T> objectsInThisLetter = splitMap.get(letter);
Collections.sort(objectsInThisLetter, comparator);
}
return splitMap;
}
/**
* This method generates implicit Lang:XX tags based on the books languages (as specified in the database).
* It checks for collisions with existing tags (i.e. it will not add a book twice to an already set tag).
* Must be called AFTER the datamodel list of books and tags has been loaded, but before the map of books by
* tags is loaded (or else it'll be reloaded)
*
* There is a configuration option to disable the treatement of language as implict tags.
*/
public static void generateImplicitLanguageTags() {
logger.debug("generateImplicitLanguageTags: Enter");
if (useLanguagesAsTags == false) {
logger.debug("generateImplicitLanguageTags: Exit - not wanted");
return;
}
// compute the latest id
int lastId = -1;
for (Tag tag : getListOfTags()) {
int id = Integer.parseInt(tag.getId());
if (lastId < id)
lastId = id;
}
Map<String, Tag> mapOfLanguageTags = new HashMap<String, Tag>();
for (Book book : getListOfBooks()) {
if (Helper.isNotNullOrEmpty(book.getBookLanguages())) {
for (Language language : book.getBookLanguages()) {
if (language != null) {
String languageTagName = IMPLICIT_LANGUAGE_TAG_PREFIX + language.getIso2();
Tag languageTag = mapOfLanguageTags.get(languageTagName.toUpperCase(Locale.ENGLISH));
if (languageTag == null) {
// check in the existing tags
for (Tag tag : getListOfTags()) {
if (tag.getName().equalsIgnoreCase(languageTagName)) {
languageTag = tag;
break;
}
}
if (languageTag == null) {
languageTag = new Tag("" + (++lastId), languageTagName);
addTag(languageTag);
}
mapOfLanguageTags.put(languageTagName.toUpperCase(Locale.ENGLISH), languageTag);
}
if (!book.getTags().contains(languageTag))
book.getTags().add(languageTag);
} else {
logger.error("generateImplicitLanguageTags: found a null language for book " + book);
for (Language language1 : book.getBookLanguages()) {
logger.error(language1);
}
}
}
}
}
mapOfBooksByTag = null;
logger.debug("generateImplicitLanguageTags: Exit");
}
/**
* Get the book languages in the form of tags.
* Only available if the apprpriate option set
* @return
*/
private static List<Tag> getBookLanguagesAsTags(int bookId) {
if (!useLanguagesAsTags) {
return null;
}
Book book = getListOfBooks().get(bookId);
assert book != null;
List<Language> bookLanguages = book.getBookLanguages();
List<Tag>tags = null;
if (bookLanguages != null) {
tags = new LinkedList<Tag>();
for (Language language : bookLanguages) {
String tagName = IMPLICIT_LANGUAGE_TAG_PREFIX + language.getIso2();
Tag tag = getMapOfTags().get(tagName);
// Do not think null return is possible, but lets play safe
if (tag != null) {
tags.add(tag);
}
}
}
return tags;
}
public static void setUseLanguagesAsTags(boolean b) {
useLanguagesAsTags = b;
}
public static boolean getUseLanguagesAsTags() { return useLanguagesAsTags; }
public static void setLibrarySortAuthor(boolean b) { librarySortAuthor = b; }
public static boolean getLibrarySortAuthor() {return librarySortAuthor; }
public static void setLibrarySortTitle(boolean b) { librarySortTitle = b; }
public static boolean getLibrarySortTitle() {return librarySortTitle; }
public static void setLibrarySortSeries(boolean b) { librarySortSeries = b; }
public static boolean getLibrarySortSeries() {return librarySortSeries; }
/**
* Get a Noiseword object given the language string
*/
public static NoiseWord getNoiseword(String language) {
Locale locale;
switch (language.length()) {
case 2: locale = Localization.Main.getLocaleFromiso2(language);
break;
case 3: locale = Localization.Main.getLocaleFromIso3(language);
break;
default:
// Bit sure this should even be ossible!
assert false : "Unexpected value for language parameter (" + language +")";
return null;
}
return getNoiseword(locale);
}
/**
* Get a Noiseword correspnding to a Language object
*
* @param lang
* @return
*/
public static NoiseWord getNoiseword(Language lang) {
if (lang == null) {
// TODO Is this option even possible?
logger.debug("Unexpected null Language parameter");
return new NoiseWord(null, new LinkedList<String>());
}
return getNoiseword(lang.getLocale());
}
/**
* Get a Noiseword object corresponding to a locale
*
* @param locale
* @return
*/
public static NoiseWord getNoiseword (Locale locale) {
if (mapOfNoisewords == null) {
mapOfNoisewords = new HashMap<Locale, NoiseWord>();
}
if (mapOfNoisewords.containsKey(locale)) {
return mapOfNoisewords.get(locale);
}
// This is a 'tweak' to ensure we have the correct internal locale
Locale ll = Localization.Main.getLocaleFromIso3(locale.getISO3Language());
if (ll == null) {
if (logger.isTraceEnabled()) logger.trace("Attempt to create Noiseword for unsupported locale " + locale.getDisplayLanguage());
return new NoiseWord(locale, null);
}
// Convert the string returned from localization to the vector format used internally
String langNoiseWords = Localization.Main.getText(ll,"i18n.noiseWords");
List<String> noiseWordList = new LinkedList<String>();
StringTokenizer st = new StringTokenizer(langNoiseWords, ",");
NoiseWord nw = null;
if (langNoiseWords != null) {
while (st.hasMoreTokens()) {
String nt = st.nextToken().toUpperCase(locale);
if (nt.length() > 1)
noiseWordList.add(nt);
}
nw = new NoiseWord(locale, noiseWordList);
mapOfNoisewords.put(locale, nw);
}
return nw;
}
/**
* Apply the specified filter to the current data model
*
* @param filter
*/
public static void filterDataModel(BookFilter filter) {
List<Book> booksCopy = new LinkedList<Book>(DataModel.getListOfBooks());
for (Book book : booksCopy) {
if (!filter.didBookPassThroughFilter(book)) {
// remove the book from the map of books by tags
for (Tag tag : book.getTags()) {
List<Book> books = DataModel.getMapOfBooksByTag().get(tag);
if (Helper.isNotNullOrEmpty(books))
books.remove(book);
if (Helper.isNullOrEmpty(books)) {
DataModel.getMapOfBooksByTag().remove(tag);
DataModel.getListOfTags().remove(tag);
}
}
DataModel.getMapOfTagsByBookId().remove(book.getId());
// remove the book from the map of books by series
Series serie = book.getSeries();
List<Book> booksInSerie = DataModel.getMapOfBooksBySeries().get(serie);
if (Helper.isNotNullOrEmpty(booksInSerie))
booksInSerie.remove(book);
if (Helper.isNullOrEmpty(booksInSerie)) {
DataModel.getMapOfBooksBySeries().remove(serie);
DataModel.getListOfSeries().remove(serie);
}
DataModel.getMapOfSeriesByBookId().remove(book.getId());
// remove the book from the map of books by author
for (Author author : book.getAuthors()) {
List<Book> booksByAuthor = DataModel.getMapOfBooksByAuthor().get(author);
if (Helper.isNotNullOrEmpty(booksByAuthor))
booksByAuthor.remove(book);
if (Helper.isNullOrEmpty(booksByAuthor)) {
DataModel.getMapOfBooksByAuthor().remove(author);
DataModel.getListOfAuthors().remove(author);
}
}
DataModel.getMapOfAuthorsByBookId().remove(book.getId());
// remove the book from the map of books by rating
BookRating rating = book.getRating();
List<Book> booksInRating = DataModel.getMapOfBooksByRating().get(rating);
if (Helper.isNotNullOrEmpty(booksInRating))
booksInRating.remove(book);
if (Helper.isNullOrEmpty(booksInRating)) {
DataModel.getMapOfBooksByRating().remove(rating);
}
// remove the book from the map of books by publisher
Publisher publisher = book.getPublisher();
List<Book> booksByPublisher = DataModel.getMapOfBooksByPublisher().get(publisher);
if (Helper.isNotNullOrEmpty(booksByPublisher))
booksByPublisher.remove(book);
if (Helper.isNullOrEmpty(booksByPublisher)) {
DataModel.getMapOfBooksByPublisher().remove(publisher);
DataModel.getListOfPublishers().remove(publisher);
}
// remove the book from the list of books
DataModel.getListOfBooks().remove(book);
// remove the book from the map of books
DataModel.getMapOfBooks().remove(book.getId());
// remove the book from the maps of XXX by bookId
DataModel.getMapOfCommentsByBookId().remove(book.getId());
DataModel.getMapOfEBookFilesByBookId().remove(book.getId());
}
}
/* check that no empty list exist */
// check that no books by tag list is empty
LinkedList<Tag> tagList = new LinkedList<Tag>(DataModel.getListOfTags());
for (Tag tag : tagList) {
List<Book> books = DataModel.getMapOfBooksByTag().get(tag);
if (Helper.isNullOrEmpty(books)) {
DataModel.getMapOfBooksByTag().remove(tag);
DataModel.getListOfTags().remove(tag);
}
}
// check that no books by series list is empty
LinkedList<Series> seriesList = new LinkedList<Series>(DataModel.getListOfSeries());
for (Series serie : seriesList) {
List<Book> booksInSerie = DataModel.getMapOfBooksBySeries().get(serie);
if (Helper.isNullOrEmpty(booksInSerie)) {
DataModel.getMapOfBooksBySeries().remove(serie);
DataModel.getListOfSeries().remove(serie);
}
}
// check that no books by author list is empty
LinkedList<Author> authorList = new LinkedList<Author>(DataModel.getListOfAuthors());
for (Author author : authorList) {
List<Book> booksByAuthor = DataModel.getMapOfBooksByAuthor().get(author);
if (Helper.isNullOrEmpty(booksByAuthor)) {
DataModel.getMapOfBooksByAuthor().remove(author);
DataModel.getListOfAuthors().remove(author);
}
}
}
}