package com.gmail.dpierron.calibre.datamodel; import com.gmail.dpierron.tools.Helper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.List; public class Author implements SplitableByLetter, Comparable<Author> { private final static Logger logger = LogManager.getLogger(Author.class); private final String id; private String name; private String sort; private String guessedLastName; private String nameForSort; // Flags // NOTE: Using byte plus bit settings is more memory efficient than using boolean types private final static byte FLAG_ALL_CLEAR = 0; private final static byte FLAG_DONE = 0x01; private final static byte FLAG_REFERENCED = 0x02; private byte flags = FLAG_ALL_CLEAR; public Author(String id, String name, String sort) { super(); this.id = id; this.name = name.replace('|', ','); /* history of change for the author.sort column : * - at the beginning, C2O was looking in this column for sort info ; if it was empty, it would be computed * - then a bug was found (577526) and I removed the load code for this column * - and then, after bug 655081 I realized that Calibre had evolved into using the column again ; so back to step one */ this.sort = sort; } public String getId() { return id; } public String getName() { return name; } public String getLastName() { if (guessedLastName == null) { String sortedName = getSort(); // sometimes getSort computes the last name for us... optimize ! if (Helper.isNotNullOrEmpty(guessedLastName)) return guessedLastName; guessedLastName = sortedName; if (Helper.isNotNullOrEmpty(getSort())) { int posOfSpace = getSort().indexOf(','); if (posOfSpace >= 0) guessedLastName = getSort().substring(0, posOfSpace); } else guessedLastName = name; } return guessedLastName; } public String getNameForSort() { if (Helper.isNullOrEmpty(nameForSort)) { nameForSort = getLastName().replaceAll(" ", "").toUpperCase(); } return nameForSort; } public String getSort() { if (Helper.isNullOrEmpty(sort)) sort = computeSort(); return sort; } /** * Derive the value we are going to use for sorting by Author * @return */ private String computeSort() { // first, let's try and find a book by this author (alone) which has a author_sort field // If so we use that value as the most likely one List<Book> books = DataModel.getMapOfBooksByAuthor().get(this); if (books != null) for (Book book : books) { if (book.hasSingleAuthor()) { String authorSort = book.getAuthorSort(); // ITIMPI: Perhaps we should also consider a value with no comma and no space as a valid author sort? if (Helper.isNotNullOrEmpty(authorSort) && authorSort.contains(",")) return authorSort; } } // We could not find an acceptable author sort value so we need to do something further // Check if there is a comma in the name field // If so assume what follows is the author sort surname int posOfSpace = name.indexOf(','); if (posOfSpace >= 0) { guessedLastName = name.substring(0, posOfSpace); return name; } // then split the author on space seaparator List<String> words = Helper.tokenize(name, " "); switch (words.size()) { case 1: // If there is only a single word then it must be the author sort guessedLastName = name; return name; case 0: logger.warn("Problem computing Author sort for author: " + name); guessedLastName = "[CALIBRE2OPDS] BAD_AUTHOR_SORT (" + name + ")"; return guessedLastName; default: // Grab the last word as the surname guessedLastName = words.get(words.size() - 1); // Remove from the array the word we have assumed is the surname words.remove(words.size() - 1); // Now construct the sort nsme we are going to use return guessedLastName + ", " + Helper.concatenateList(" ", words); } } public String toString() { return getId() + " - " + getName(); } public String getTitleToSplitByLetter() { return DataModel.getLibrarySortAuthor() ? getName() : getNameForSort(); } /* Comparable interface, used to sort an authors list */ public int compareTo(Author o) { if (o == null) return 1; else { return Helper.trueStringCompare(getSort(), o.getSort()); } } public void setDone() { flags |= FLAG_DONE; } public boolean isDone () { return ((flags & FLAG_DONE) != 0); } public void setReferenced() { flags |= FLAG_REFERENCED; } public boolean isReferenced () { return ((flags & FLAG_REFERENCED) != 0); } }