/*
* Copyright 2015 Dmitry Monakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* 2/13/15.
*/
package monakhv.samlib.db;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.PreparedQuery;
import com.j256.ormlite.stmt.QueryBuilder;
import monakhv.samlib.db.entity.*;
import monakhv.samlib.log.Log;
import javax.inject.Inject;
import java.sql.SQLException;
import java.util.List;
/**
* COL_ID+" integer primary key autoincrement, "+
* COL_NAME+" text, "+
* COL_URL +" text UNIQUE NOT NULL, "+
* COL_isnew+" BOOLEAN DEFAULT '0' NOT NULL,"+
* COL_mtime+" timestamp "+
*/
public class AuthorController implements AbstractController<Author> {
private static final String DEBUG_TAG = "AuthorController";
private final BookController bookCtl;
private final Tag2AuthorController t2aCtl;
private final TagController tagCtl;
private final GroupBookController grpCtl;
private final Dao<Author, Integer> dao;
private final Dao<Tag2Author, Integer> t2aDao;
@Inject
public AuthorController(DaoBuilder sql) {
dao = sql.getAuthorDao();
t2aDao = sql.getT2aDao();
this.bookCtl = new BookController(sql);
this.t2aCtl = new Tag2AuthorController(sql);
this.tagCtl = new TagController(sql);
grpCtl = new GroupBookController(sql);
}
/**
* Mark Author and all it's book as read
*
* @param a Author object
* @return id of the Author
*/
public int markRead(Author a) {
a.setIsNew(false);
int ires = update(a);
loadBooks(a);
for (Book book : a.getBooks()) {
if (book.isIsNew()) {
bookCtl.markRead(book);
}
}
return ires;
}
/**
* Update Author object
*
* @param author Author to update
* @return id
*/
@Override
public int update(Author author) {
int res;
try {
res = dao.update(author);
} catch (SQLException e) {
Log.e(DEBUG_TAG, "can not update: ", e);
return -1;
}
if (!author.isBookLoaded()) {//books are not loaded since update the author only without books
Log.i(DEBUG_TAG, "Books are not loaded exiting");
return res;
}
grpCtl.operate(author);
bookCtl.operate(author);
for (GroupBook groupBook : grpCtl.getByAuthor(author)) {
bookCtl.updateGroupNewNumber(groupBook);
}
return res;
}
/**
* Make persist new Author object
*
* @param author Author to persist
* @return id
*/
@Override
public long insert(Author author) {
try {
dao.create(author);
} catch (SQLException e) {
Log.e(DEBUG_TAG, "can not insert: ", e);
return -1;
}
//Load Object from DB
Author a = getByUrl(author.getUrl());
a.setGroupBooks(author.getGroupBooks());//Groups to add
a.setBooks(author.getBooks());//Books to add
grpCtl.operate(a);//add Groups
bookCtl.operate(a);//add Books
return a.getId();
}
/**
* Delete author object from Data base
*
* @param author Author to delete
* @return id
*/
@Override
public int delete(Author author) {
loadBooks(author);
loadGroupBooks(author);
//Delete book of the author first
bookCtl.operate(author);
//Delete Tag2Author
t2aCtl.deleteByAuthor(author);
grpCtl.operate(author);
//Delete Author
int res;
try {
res = dao.delete(author);
} catch (SQLException e) {
Log.e(DEBUG_TAG, "can not delete: ", e);
return -1;
}
return res;
}
@Override
public List<Author> getAll() {
return getAll(SamLibConfig.TAG_AUTHOR_ALL, null);
}
public Author getByUrl(String url) {
QueryBuilder<Author, Integer> statement = dao.queryBuilder();
List<Author> rr = query(getPrepared(statement, SQLController.COL_URL, url));
if (rr == null || rr.size() != 1) {
return null;
}
return rr.get(0);
}
private List<Author> query(PreparedQuery<Author> prep) {
List<Author> rr;
if (prep == null) {
Log.e(DEBUG_TAG, "query: prepare error");
}
try {
rr = dao.query(prep);
} catch (SQLException e) {
Log.e(DEBUG_TAG, "query: query error");
return null;
}
return rr;
}
/**
* Very general method to make SQL query like this
* <i>Select * from Author WHERE x=y</i>
*
* @param cb QueryBuilder could be make sorted calls
* @param ColumnName Column name for WHERE or null when select ALL items
* @param object Object for WHERE =
* @return preparedQuery
*/
@SuppressWarnings("unchecked")
private PreparedQuery<Author> getPrepared(QueryBuilder<Author, Integer> cb, String ColumnName, Object object) {
try {
if (ColumnName != null) {
if (ColumnName.equalsIgnoreCase(SQLController.COL_ID) && object instanceof QueryBuilder) {
//Log.d(DEBUG_TAG, "PreparedQuery: running where IN query");
cb.where().in(ColumnName, (QueryBuilder<Tag2Author, Integer>) object);
} else {
Log.d(DEBUG_TAG, "PreparedQuery: running where EQ query");
cb.where().eq(ColumnName, object);
}
}
return cb.prepare();
} catch (SQLException e) {
Log.e(DEBUG_TAG, "getPrepared error: " + cb.toString(), e);
return null;
}
}
/**
* Make human readable String with list of all tags for the authors
*
* @param authors list of the authors
*/
private void updateAuthorTags(List<Author> authors) {
for (Author author : authors) {
updateTags(author);
}
}
public void updateAuthorTags() {
updateAuthorTags(getAll());
}
public void updateTags(Author author) {
String allTagString = getAllTagString(author);
author.setAll_tags_name(allTagString);
update(author);
}
public String getAllTagString(Author author) {
if (author.getTag2Authors() == null) {
Log.e(DEBUG_TAG, "getAllTagString: T2A Collection is NULL for Author " + author.getName());
return null;
}
int num = author.getTag2Authors().size();
int i = 1;
StringBuilder sb = new StringBuilder();
//Log.d(DEBUG_TAG, "updateTags: author " + author.getName() + " has " + num + " tags");
for (Tag2Author t2a : author.getTag2Authors()) {
sb.append(tagCtl.getById(t2a.getTag().getId()).getName());
if (i < num) {
sb.append(", ");
}
++i;
}
return sb.toString();
}
/**
* the main method to query authors
* <p>
* SamLibConfig.TAG_AUTHOR_ALL - all authors
* SamLibConfig.TAG_AUTHOR_NEW - authors with new books
*
* @param iSelectTag ALL, New or TAG-id
* @param rowSort -- SQL order part statement - can be null
* @return list of the authors
*/
public synchronized List<Author> getAll(int iSelectTag, String rowSort) {
PreparedQuery<Author> prep = getPrepared(iSelectTag, rowSort);
if (prep == null) {
Log.e(DEBUG_TAG, "getAll: prepare error for tag Id: " + iSelectTag);
return null;
}
return query(prep);
}
private PreparedQuery<Author> getPrepared(int iSel, String rowSort) {
QueryBuilder<Author, Integer> statement = dao.queryBuilder();
if (rowSort != null) {
statement.orderByRaw(rowSort);
}
if (iSel == SamLibConfig.TAG_AUTHOR_ALL) {//return ALL Authors
//Log.d(DEBUG_TAG, "getPrepared: query ALL Authors");
return getPrepared(statement, null, null);
}
if (iSel == SamLibConfig.TAG_AUTHOR_NEW) {//return Authors with new Books
return getPrepared(statement, SQLController.COL_isnew, true);
}
Tag tag = tagCtl.getById(iSel);
if (tag == null) {
Log.e(DEBUG_TAG, "getPrepared: wrong tag: >" + iSel + "<");
return null;
}
QueryBuilder<Tag2Author, Integer> t2aqb = t2aDao.queryBuilder();
t2aqb.selectColumns(SQLController.COL_T2A_AUTHORID);
try {
t2aqb.where().eq(SQLController.COL_T2A_TAGID, tag);
} catch (SQLException e) {
Log.e(DEBUG_TAG, "getPrepared: SQL Error");
return null;
}
return getPrepared(statement, SQLController.COL_ID, t2aqb);
}
@Override
public Author getById(long id) {
Integer dd = (int) id;
Author a;
try {
a = dao.queryForId(dd);
} catch (SQLException e) {
Log.e(DEBUG_TAG, "getById - Error", e);
return null;
}
return a;
}
public BookController getBookController() {
return bookCtl;
}
public TagController getTagController() {
return tagCtl;
}
public GroupBookController getGroupBookController() {
return grpCtl;
}
/**
* Set author new status according to the its book status
*
* @param author Author object
* @return true if the author was updated
*/
public boolean testMarkRead(Author author) {
Author a = getById(author.getId());
boolean rr = getReadStatus(a);
if (a.isIsNew() && rr) {
return false;
}
if (!a.isIsNew() && !rr) {
return false;
}
a.setIsNew(rr);
a.setBookLoaded(false);
update(a);
author.setIsNew(a.isIsNew());
return true;
}
/**
* Whether the author has unread books or not
*
* @param a Author object
* @return true if the author has at least one unread book
*/
public boolean getReadStatus(Author a) {
loadBooks(a);
for (Book book : a.getBooks()) {
if (book.isIsNew()) {
return true;
}
}
return false;
}
/**
* Making tags for the Author according to th given list
*
* @param author the Author
* @param tags list of tags
* @return true if database sync was done
*/
public boolean syncTags(Author author, List<Tag> tags) {
boolean bRes = t2aCtl.sync(author, tags);
author = getById(author.getId());
if (bRes) {
//Log.d(DEBUG_TAG, "syncTags: making update for All_Tags_String for " + author.getName());
updateTags(author);
}
return bRes;
}
/**
* Find books of the author and load them into object
*
* @param a Author object to load books for
*/
public void loadBooks(Author a) {
a.setBooks(getBookController().getBooksByAuthor(a));
a.setBookLoaded(true);
}
public void loadGroupBooks(Author a) {
a.setGroupBooks(grpCtl.getByAuthor(a));
}
/**
* Ugly hack to cleanup database
* DELETE from Book where author_id=0
*/
public void cleanBooks() {
try {
dao.executeRawNoArgs("DELETE from " + SQLController.TABLE_BOOKS + " where " + SQLController.COL_BOOK_AUTHOR_ID + " = 0");
} catch (SQLException e) {
Log.e(DEBUG_TAG, "Delete clean books: ", e);
}
}
}