/*
* @copyright 2013 Philip Warner
* @license GNU General Public License
*
* This file is part of Book Catalogue.
*
* Book Catalogue 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 3 of the License, or
* (at your option) any later version.
*
* Book Catalogue 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 Book Catalogue. If not, see <http://www.gnu.org/licenses/>.
*/
package com.eleybourn.bookcatalogue;
import java.util.ArrayList;
import android.database.Cursor;
import android.os.Bundle;
import com.eleybourn.bookcatalogue.datamanager.DataAccessor;
import com.eleybourn.bookcatalogue.datamanager.DataManager;
import com.eleybourn.bookcatalogue.datamanager.Datum;
import com.eleybourn.bookcatalogue.utils.Logger;
import com.eleybourn.bookcatalogue.utils.Utils;
/**
* Represents the underlying data for a book.
*
* @author pjw
*/
public class BookData extends DataManager {
/** Row ID for book */
private long mRowId;
/** Key for special field */
private static final String KEY_BOOKSHELF_LIST = "+BookshelfList";
/** Key for special field */
private static final String KEY_BOOKSHELF_TEXT = "+BookshelfText";
/** Key for special field */
public static final String KEY_ANTHOLOGY = "+IsAnthology";
/** Constructor */
public BookData() {
this(0L, ((Bundle)null));
}
/** Constructor */
public BookData(Long rowId) {
this(rowId, ((Bundle)null));
}
/**
* Constructor
*
* @param src Bundle with book data (may be null)
*/
public BookData(Bundle src) {
this(0L, src);
}
/**
* Constructor
*
* @param rowId ID of book (may be 0 for new)
* @param src Bundle with book data (may be null)
*/
public BookData(Long rowId, Bundle src) {
// Save the row, if possible
if (rowId == null) {
mRowId = 0;
} else {
mRowId = rowId;
}
// Load from bundle or database
if (src != null) {
putAll(src);
} else if (mRowId > 0) {
loadFromDb();
}
// Create special validators
initValidators();
}
/**
* Erase everything in this instance and reset the special handlers
*
* @return self, for chaining
*/
@Override
public DataManager clear() {
super.clear();
// Create special validators
initValidators();
return this;
}
/** Accessor */
public void setBookshelfList(String encodedList) {
putString(KEY_BOOKSHELF_LIST, encodedList);
}
/** Accessor */
public String getBookshelfList() {
return getString(KEY_BOOKSHELF_LIST);
}
/** Accessor. Return a formatted list of books. */
public String getBookshelfText() {
String list = getBookshelfList();
ArrayList<String> items = Utils.decodeList(list, BookDetailsAbstract.BOOKSHELF_SEPERATOR);
if (items.size() == 0)
return "";
StringBuilder text = new StringBuilder(items.get(0));
for(int i = 1; i < items.size(); i++) {
text.append(", ");
text.append(items.get(i));
}
return text.toString();
}
/**
* Create the list of bookshlves in the underlying data
*
* @param db Database connection
*
* @return The list
*/
private String getBookshelfListFromDb(CatalogueDBAdapter db) {
Cursor bookshelves = db.fetchAllBookshelvesByBook(getRowId());
try {
String bookshelves_list = "";
while (bookshelves.moveToNext()) {
String name = bookshelves.getString(bookshelves.getColumnIndex(CatalogueDBAdapter.KEY_BOOKSHELF));
String encoded_name = Utils.encodeListItem(name, BookDetailsAbstract.BOOKSHELF_SEPERATOR);
if (bookshelves_list.equals("")) {
bookshelves_list = encoded_name;
} else {
bookshelves_list += BookDetailsAbstract.BOOKSHELF_SEPERATOR + encoded_name;
}
}
return bookshelves_list;
} finally {
if (bookshelves != null)
bookshelves.close();
}
}
/** Accessor */
public long getRowId() {
return mRowId;
}
/** Reload all data from DB */
public BookData reload() {
loadFromDb();
return this;
}
/**
* Load the book details from the database
*/
private void loadFromDb() {
long rowId = getRowId();
// If ID = 0, no details in DB
if (rowId == 0)
return;
// Connect to DB and get cursor for bok details
CatalogueDBAdapter db = new CatalogueDBAdapter(BookCatalogueApp.context);
db.open();
try {
BooksCursor book = db.fetchBookById(getRowId());
try {
// Put all cursor fields in collection
putAll(book);
// Get author, series, bookshelf and anthology title lists
setAuthorList(db.getBookAuthorList(getRowId()));
setSeriesList(db.getBookSeriesList(getRowId()));
setBookshelfList(getBookshelfListFromDb(db));
setAnthologyTitles(db.getBookAnthologyTitleList(getRowId()));
} catch (Exception e) {
Logger.logError(e);
} finally {
if (book != null)
book.close();
}
} finally {
db.close();
}
}
/** Special Accessor */
public ArrayList<Author> getAuthorList() {
return getAuthors();
}
/** Special Accessor */
public void setAnthologyTitles(ArrayList<AnthologyTitle> list) {
putSerializable(CatalogueDBAdapter.KEY_ANTHOLOGY_TITLE_ARRAY, list);
}
/** Special Accessor */
public void setAuthorList(ArrayList<Author> list) {
putSerializable(CatalogueDBAdapter.KEY_AUTHOR_ARRAY, list);
}
/** Special Accessor */
public ArrayList<Series> getSeriesList() {
return getSeries(); // mSeriesList;
}
/** Special Accessor */
public void setSeriesList(ArrayList<Series> list) {
putSerializable(CatalogueDBAdapter.KEY_SERIES_ARRAY, list);
}
/**
* Special Accessor.
*
* Build a formatted string for author list.
*/
public String getAuthorTextShort() {
String newText;
ArrayList<Author> list = getAuthorList();
if (list.size() == 0) {
newText = null;
} else {
newText = list.get(0).getDisplayName();
if (list.size() > 1) {
newText += " " + BookCatalogueApp.context.getResources().getString(R.string.and_others);
}
}
return newText;
}
/**
* Special Accessor.
*
* Build a formatted string for series list.
*/
public String getSeriesTextShort() {
String newText;
ArrayList<Series> list = getSeriesList();
if (list.size() == 0) {
newText = null;
} else {
newText = list.get(0).getDisplayName();
if (list.size() > 1) {
newText += " " + BookCatalogueApp.context.getResources().getString(R.string.and_others);
}
}
return newText;
}
/**
* Build any special purpose validators
*/
private void initValidators() {
addValidator(CatalogueDBAdapter.KEY_TITLE, nonBlankValidator);
addValidator(CatalogueDBAdapter.KEY_ANTHOLOGY_MASK, integerValidator);
/* Anthology needs special handling, and we use a formatter to do this. If the original
* value was 0 or 1, then setting/clearing it here should just set the new value to 0 or 1.
* However...if if the original value was 2, then we want setting/clearing to alternate
* between 2 and 0, not 1 and 0.
* So, despite if being a checkbox, we use an integerValidator and use a special formatter.
* We also store it in the tag field so that it is automatically serialized with the
* activity. */
addAccessor(KEY_ANTHOLOGY, new DataAccessor() {
@Override
public Object get(DataManager data, Datum datum, Bundle rawData) {
Integer mask = data.getInt(CatalogueDBAdapter.KEY_ANTHOLOGY_MASK);
return mask != 0 ? "1" : "0";
}
@Override
public void set(DataManager data, Datum datum, Bundle rawData, Object value) {
Integer mask = getInt(CatalogueDBAdapter.KEY_ANTHOLOGY_MASK);
// Parse the string the CheckBox returns us (0 or 1)
if (Utils.objectToBoolean(value)) {
mask |= 1;
} else {
mask &= 0xFFFFFFFE;
}
putInt(CatalogueDBAdapter.KEY_ANTHOLOGY_MASK, mask);
}
@Override
public boolean isPresent(DataManager data, Datum datum, Bundle rawData) {
return rawData.containsKey(CatalogueDBAdapter.KEY_ANTHOLOGY_MASK);
}
});
// Make a formatted list of bookshelves
addAccessor(KEY_BOOKSHELF_TEXT, new DataAccessor() {
@Override
public Object get(DataManager data, Datum datum, Bundle rawData) {
return getBookshelfText();
}
@Override
public void set(DataManager data, Datum datum, Bundle rawData, Object value) {
throw new RuntimeException("Bookshelf Text can not be set");
}
@Override
public boolean isPresent(DataManager data, Datum datum, Bundle rawData) {
return !getBookshelfText().equals("");
}
});
// Whenever the row ID is written, make sure mRowId is updated.
addAccessor(CatalogueDBAdapter.KEY_ROWID, new DataAccessor() {
@Override
public Object get(DataManager data, Datum datum, Bundle rawData) {
return Datum.objectToLong(rawData.get(datum.getKey()));
}
@Override
public void set(DataManager data, Datum datum, Bundle rawData, Object value) {
rawData.putLong(datum.getKey(), Datum.objectToLong(value));
mRowId = rawData.getLong(datum.getKey());
}
@Override
public boolean isPresent(DataManager data, Datum datum, Bundle rawData) {
return true;
}
});
addValidator("list_price", blankOrFloatValidator);
addValidator(CatalogueDBAdapter.KEY_PAGES, blankOrIntegerValidator);
}
/**
* Utility routine to get an author list from a data manager
*
* @param i Intent with author list
* @return List of authors
*/
@SuppressWarnings("unchecked")
public ArrayList<AnthologyTitle> getAnthologyTitles() {
ArrayList<AnthologyTitle> list = (ArrayList<AnthologyTitle>) getSerializable(CatalogueDBAdapter.KEY_ANTHOLOGY_TITLE_ARRAY);
if (list == null) {
list = new ArrayList<AnthologyTitle>();
}
return list;
}
/**
* Utility routine to get an author list from a data manager
*
* @param i Intent with author list
* @return List of authors
*/
@SuppressWarnings("unchecked")
private ArrayList<Author> getAuthors() {
ArrayList<Author> list = (ArrayList<Author>) getSerializable(CatalogueDBAdapter.KEY_AUTHOR_ARRAY);
if (list == null) {
list = new ArrayList<Author>();
}
return list;
}
/**
* Utility routine to get an author list from a data manager
*
* @param i Intent with author list
* @return List of authors
*/
@SuppressWarnings("unchecked")
private ArrayList<Series> getSeries() {
ArrayList<Series> list = (ArrayList<Series>) getSerializable(CatalogueDBAdapter.KEY_SERIES_ARRAY);
if (list == null) {
list = new ArrayList<Series>();
}
return list;
}
/** Convenience Accessor */
public boolean isRead() {
int val = getInt(CatalogueDBAdapter.KEY_READ);
return val != 0;
}
/** Convenience Accessor */
public boolean isSigned() {
int val = getInt(CatalogueDBAdapter.KEY_SIGNED);
return val != 0;
}
/**
* Update author details from DB
*
* @param db Database connection
*/
public void refreshAuthorList(CatalogueDBAdapter db) {
ArrayList<Author> list = getAuthorList();
for(Author a : list) {
db.refreshAuthor(a);
}
setAuthorList(list);
}
/**
* Cleanup thumbnails from underlying data
*/
public void cleanupThumbnails() {
Utils.cleanupThumbnails(mBundle);
}
/**
* Get the underlying raw data.
* DO NOT UPDATE THIS! IT SHOULD BE USED FOR READING DATA ONLY.
* @return
*/
public Bundle getRawData() {
return mBundle;
}
}