mirror of
https://github.com/geometer/FBReaderJ.git
synced 2025-10-05 02:39:23 +02:00
Merge branch 'master' into library-service
Conflicts: src/org/geometerplus/fbreader/library/Library.java
This commit is contained in:
commit
83e5045a09
14 changed files with 231 additions and 59 deletions
|
@ -134,6 +134,19 @@ public class BookCollectionShadow extends AbstractBookCollection implements Serv
|
|||
}
|
||||
}
|
||||
|
||||
public synchronized List<Book> books(Tag tag) {
|
||||
if (myInterface == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return SerializerUtil.deserializeBookList(
|
||||
myInterface.booksForTag(Util.tagToString(tag))
|
||||
);
|
||||
} catch (RemoteException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized List<Book> books(String pattern) {
|
||||
if (myInterface == null) {
|
||||
return Collections.emptyList();
|
||||
|
@ -220,12 +233,16 @@ public class BookCollectionShadow extends AbstractBookCollection implements Serv
|
|||
if (myInterface == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
//try {
|
||||
// TODO: implement
|
||||
try {
|
||||
final List<String> strings = myInterface.tags();
|
||||
final List<Tag> tags = new ArrayList<Tag>(strings.size());
|
||||
for (String s : strings) {
|
||||
tags.add(Util.stringToTag(s));
|
||||
}
|
||||
return tags;
|
||||
} catch (RemoteException e) {
|
||||
return Collections.emptyList();
|
||||
//} catch (RemoteException e) {
|
||||
// return Collections.emptyList();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized List<String> series() {
|
||||
|
|
|
@ -11,6 +11,7 @@ interface LibraryInterface {
|
|||
int size();
|
||||
List<String> books();
|
||||
List<String> booksForAuthor(in String author);
|
||||
List<String> booksForTag(in String tag);
|
||||
List<String> booksForPattern(in String pattern);
|
||||
List<String> recentBooks();
|
||||
List<String> favorites();
|
||||
|
@ -19,6 +20,8 @@ interface LibraryInterface {
|
|||
String getRecentBook(in int index);
|
||||
|
||||
List<String> authors();
|
||||
List<String> series();
|
||||
List<String> tags();
|
||||
|
||||
boolean saveBook(in String book, in boolean force);
|
||||
void removeBook(in String book, in boolean deleteFromDisk);
|
||||
|
|
|
@ -128,6 +128,10 @@ public class LibraryService extends Service {
|
|||
return SerializerUtil.serializeBookList(myCollection.books(Util.stringToAuthor(author)));
|
||||
}
|
||||
|
||||
public List<String> booksForTag(String tag) {
|
||||
return SerializerUtil.serializeBookList(myCollection.books(Util.stringToTag(tag)));
|
||||
}
|
||||
|
||||
public List<String> booksForPattern(String pattern) {
|
||||
return SerializerUtil.serializeBookList(myCollection.books(pattern));
|
||||
}
|
||||
|
@ -161,6 +165,19 @@ public class LibraryService extends Service {
|
|||
return strings;
|
||||
}
|
||||
|
||||
public List<String> series() {
|
||||
return myCollection.series();
|
||||
}
|
||||
|
||||
public List<String> tags() {
|
||||
final List<Tag> tags = myCollection.tags();
|
||||
final List<String> strings = new ArrayList<String>(tags.size());
|
||||
for (Tag t : tags) {
|
||||
strings.add(Util.tagToString(t));
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
public boolean saveBook(String book, boolean force) {
|
||||
return myCollection.saveBook(SerializerUtil.deserializeBook(book), force);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.geometerplus.android.fbreader.libraryService;
|
||||
|
||||
import org.geometerplus.fbreader.book.Author;
|
||||
import org.geometerplus.fbreader.book.Tag;
|
||||
|
||||
abstract class Util {
|
||||
static String authorToString(Author author) {
|
||||
|
@ -34,4 +35,17 @@ abstract class Util {
|
|||
return Author.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static String tagToString(Tag tag) {
|
||||
return tag.toString("\000");
|
||||
}
|
||||
|
||||
static Tag stringToTag(String string) {
|
||||
final String[] splitted = string.split("\000");
|
||||
if (splitted.length > 0) {
|
||||
return Tag.getTag(splitted);
|
||||
} else {
|
||||
return Tag.NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,6 +210,18 @@ public class BookCollection extends AbstractBookCollection {
|
|||
return filtered;
|
||||
}
|
||||
|
||||
public List<Book> books(Tag tag) {
|
||||
final boolean isNull = Tag.NULL.equals(tag);
|
||||
final LinkedList<Book> filtered = new LinkedList<Book>();
|
||||
for (Book b : books()) {
|
||||
final List<Tag> bookTags = b.tags();
|
||||
if (isNull && bookTags.isEmpty() || bookTags.contains(tag)) {
|
||||
filtered.add(b);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public List<Book> books(String pattern) {
|
||||
if (pattern == null || pattern.length() == 0) {
|
||||
return Collections.emptyList();
|
||||
|
@ -262,7 +274,12 @@ public class BookCollection extends AbstractBookCollection {
|
|||
final Set<Tag> tags = new TreeSet<Tag>();
|
||||
synchronized (myBooksByFile) {
|
||||
for (Book book : myBooksByFile.values()) {
|
||||
tags.addAll(book.tags());
|
||||
final List<Tag> bookTags = book.tags();
|
||||
if (bookTags.isEmpty()) {
|
||||
tags.add(Tag.NULL);
|
||||
} else {
|
||||
tags.addAll(book.tags());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayList<Tag>(tags);
|
||||
|
|
|
@ -45,6 +45,7 @@ public interface IBookCollection {
|
|||
int size();
|
||||
List<Book> books();
|
||||
List<Book> books(Author author);
|
||||
List<Book> books(Tag tag);
|
||||
List<Book> books(String pattern);
|
||||
List<Book> recentBooks();
|
||||
List<Book> favorites();
|
||||
|
|
|
@ -22,6 +22,8 @@ package org.geometerplus.fbreader.book;
|
|||
import java.util.HashMap;
|
||||
|
||||
public final class Tag {
|
||||
public static final Tag NULL = new Tag(null, "");
|
||||
|
||||
private static final HashMap<Tag,Tag> ourTagSet = new HashMap<Tag,Tag>();
|
||||
|
||||
public static Tag getTag(Tag parent, String name) {
|
||||
|
|
|
@ -45,14 +45,13 @@ public class AuthorListTree extends FirstLevelTree {
|
|||
@Override
|
||||
public boolean onBookEvent(BookEvent event, Book book) {
|
||||
switch (event) {
|
||||
default:
|
||||
case Added:
|
||||
{
|
||||
final List<Author> bookAuthors = book.authors();
|
||||
boolean changed = false;
|
||||
if (bookAuthors.isEmpty()) {
|
||||
changed &= createAuthorSubTree(Author.NULL);
|
||||
} else for (Author a : Collection.authors()) {
|
||||
} else for (Author a : bookAuthors) {
|
||||
changed &= createAuthorSubTree(a);
|
||||
}
|
||||
return changed;
|
||||
|
@ -60,6 +59,7 @@ public class AuthorListTree extends FirstLevelTree {
|
|||
case Removed:
|
||||
// TODO: implement
|
||||
return false;
|
||||
default:
|
||||
case Updated:
|
||||
// TODO: implement
|
||||
return false;
|
||||
|
|
|
@ -39,10 +39,8 @@ public class AuthorTree extends LibraryTree {
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return
|
||||
Author != Author.NULL ?
|
||||
Author.DisplayName :
|
||||
Library.resource().getResource("unknownAuthor").getValue();
|
||||
return Author.NULL.equals(Author)
|
||||
? Library.resource().getResource("unknownAuthor").getValue() : Author.DisplayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,7 +50,7 @@ public class AuthorTree extends LibraryTree {
|
|||
|
||||
@Override
|
||||
protected String getSortKey() {
|
||||
if (Author == null) {
|
||||
if (Author.NULL.equals(Author)) {
|
||||
return null;
|
||||
}
|
||||
return new StringBuilder()
|
||||
|
@ -90,12 +88,16 @@ public class AuthorTree extends LibraryTree {
|
|||
switch (event) {
|
||||
case Added:
|
||||
return containsBook(book) && createBookSubTree(book);
|
||||
case Removed:
|
||||
// TODO: implement
|
||||
case Updated:
|
||||
// TODO: implement
|
||||
default:
|
||||
return super.onBookEvent(event, book);
|
||||
}
|
||||
}
|
||||
|
||||
boolean createBookSubTree(Book book) {
|
||||
private boolean createBookSubTree(Book book) {
|
||||
final SeriesInfo seriesInfo = book.getSeriesInfo();
|
||||
if (seriesInfo != null) {
|
||||
return getSeriesSubTree(seriesInfo.Title).createBookInSeriesSubTree(book);
|
||||
|
|
|
@ -53,7 +53,7 @@ public class FavoritesTree extends FirstLevelTree {
|
|||
new BookWithAuthorsTree(this, book);
|
||||
return true;
|
||||
} if (event == BookEvent.Updated && !Collection.isFavorite(book)) {
|
||||
return removeBook(book, false);
|
||||
return removeBook(book);
|
||||
} else {
|
||||
return super.onBookEvent(event, book);
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ public final class Library {
|
|||
new RecentBooksTree(myRootTree);
|
||||
new AuthorListTree(myRootTree);
|
||||
new FirstLevelTree(myRootTree, LibraryTree.ROOT_BY_TITLE);
|
||||
new FirstLevelTree(myRootTree, LibraryTree.ROOT_BY_TAG);
|
||||
new TagListTree(myRootTree);
|
||||
new FileFirstLevelTree(myRootTree);
|
||||
}
|
||||
|
||||
|
@ -166,16 +166,6 @@ public final class Library {
|
|||
return parentTree != null ? (LibraryTree)parentTree.getSubTree(key.Id) : null;
|
||||
}
|
||||
|
||||
private final List<?> myNullList = Collections.singletonList(null);
|
||||
|
||||
private LibraryTree getTagTree(Tag tag) {
|
||||
if (tag == null || tag.Parent == null) {
|
||||
return getFirstLevelTree(LibraryTree.ROOT_BY_TAG).getTagSubTree(tag);
|
||||
} else {
|
||||
return getTagTree(tag.Parent).getTagSubTree(tag);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void addBookToLibrary(Book book) {
|
||||
synchronized (myBooks) {
|
||||
final Book existing = myBooks.get(book.getId());
|
||||
|
@ -205,24 +195,16 @@ public final class Library {
|
|||
if (letter != null) {
|
||||
final TitleTree tree =
|
||||
getFirstLevelTree(LibraryTree.ROOT_BY_TITLE).getTitleSubTree(letter);
|
||||
tree.getBookWithAuthorsSubTree(book);
|
||||
tree.createBookWithAuthorsSubTree(book);
|
||||
}
|
||||
} else {
|
||||
getFirstLevelTree(LibraryTree.ROOT_BY_TITLE).getBookWithAuthorsSubTree(book);
|
||||
}
|
||||
|
||||
List<Tag> tags = book.tags();
|
||||
if (tags.isEmpty()) {
|
||||
tags = (List<Tag>)myNullList;
|
||||
}
|
||||
for (Tag t : tags) {
|
||||
getTagTree(t).getBookWithAuthorsSubTree(book);
|
||||
getFirstLevelTree(LibraryTree.ROOT_BY_TITLE).createBookWithAuthorsSubTree(book);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
final SearchResultsTree found = (SearchResultsTree)getFirstLevelTree(LibraryTree.ROOT_FOUND);
|
||||
if (found != null && book.matches(found.getPattern())) {
|
||||
found.getBookWithAuthorsSubTree(book);
|
||||
found.createBookWithAuthorsSubTree(book);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +212,7 @@ public final class Library {
|
|||
private void removeFromTree(String rootId, Book book) {
|
||||
final FirstLevelTree tree = getFirstLevelTree(rootId);
|
||||
if (tree != null) {
|
||||
tree.removeBook(book, false);
|
||||
tree.removeBook(book);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,7 +226,6 @@ public final class Library {
|
|||
removeFromTree(LibraryTree.ROOT_FOUND, book);
|
||||
removeFromTree(LibraryTree.ROOT_BY_TITLE, book);
|
||||
removeFromTree(LibraryTree.ROOT_BY_SERIES, book);
|
||||
removeFromTree(LibraryTree.ROOT_BY_TAG, book);
|
||||
addBookToLibrary(book);
|
||||
fireModelChangedEvent(ChangeListener.Code.BookAdded);
|
||||
}
|
||||
|
@ -292,7 +273,7 @@ public final class Library {
|
|||
newSearchResults = new SearchResultsTree(myRootTree, LibraryTree.ROOT_FOUND, pattern);
|
||||
fireModelChangedEvent(ChangeListener.Code.Found);
|
||||
}
|
||||
newSearchResults.getBookWithAuthorsSubTree(book);
|
||||
newSearchResults.createBookWithAuthorsSubTree(book);
|
||||
fireModelChangedEvent(ChangeListener.Code.BookAdded);
|
||||
}
|
||||
}
|
||||
|
@ -320,6 +301,5 @@ public final class Library {
|
|||
return;
|
||||
}
|
||||
Collection.removeBook(book, (removeMode & REMOVE_FROM_DISK) != 0);
|
||||
myRootTree.removeBook(book, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,13 +63,14 @@ public abstract class LibraryTree extends FBTree {
|
|||
return true;
|
||||
}
|
||||
|
||||
TagTree getTagSubTree(Tag tag) {
|
||||
boolean createTagSubTree(Tag tag) {
|
||||
final TagTree temp = new TagTree(Collection, tag);
|
||||
int position = Collections.binarySearch(subTrees(), temp);
|
||||
if (position >= 0) {
|
||||
return (TagTree)subTrees().get(position);
|
||||
return false;
|
||||
} else {
|
||||
return new TagTree(this, tag, - position - 1);
|
||||
new TagTree(this, tag, - position - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,13 +84,14 @@ public abstract class LibraryTree extends FBTree {
|
|||
}
|
||||
}
|
||||
|
||||
BookWithAuthorsTree getBookWithAuthorsSubTree(Book book) {
|
||||
boolean createBookWithAuthorsSubTree(Book book) {
|
||||
final BookWithAuthorsTree temp = new BookWithAuthorsTree(Collection, book);
|
||||
int position = Collections.binarySearch(subTrees(), temp);
|
||||
if (position >= 0) {
|
||||
return (BookWithAuthorsTree)subTrees().get(position);
|
||||
return false;
|
||||
} else {
|
||||
return new BookWithAuthorsTree(this, book, - position - 1);
|
||||
new BookWithAuthorsTree(this, book, - position - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +105,7 @@ public abstract class LibraryTree extends FBTree {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean removeBook(Book book, boolean recursively) {
|
||||
public boolean removeBook(Book book) {
|
||||
final LinkedList<FBTree> toRemove = new LinkedList<FBTree>();
|
||||
for (FBTree tree : this) {
|
||||
if (tree instanceof BookTree && ((BookTree)tree).Book.equals(book)) {
|
||||
|
@ -112,12 +114,6 @@ public abstract class LibraryTree extends FBTree {
|
|||
}
|
||||
for (FBTree tree : toRemove) {
|
||||
tree.removeSelf();
|
||||
FBTree parent = tree.Parent;
|
||||
if (recursively) {
|
||||
for (; parent != null && !parent.hasChildren(); parent = parent.Parent) {
|
||||
parent.removeSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
return !toRemove.isEmpty();
|
||||
}
|
||||
|
@ -128,7 +124,7 @@ public abstract class LibraryTree extends FBTree {
|
|||
case Added:
|
||||
return false;
|
||||
case Removed:
|
||||
return removeBook(book, true);
|
||||
return removeBook(book);
|
||||
case Updated:
|
||||
{
|
||||
boolean changed = false;
|
||||
|
|
72
src/org/geometerplus/fbreader/library/TagListTree.java
Normal file
72
src/org/geometerplus/fbreader/library/TagListTree.java
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2013 Geometer Plus <contact@geometerplus.com>
|
||||
*
|
||||
* 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.library;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.geometerplus.fbreader.book.*;
|
||||
|
||||
public class TagListTree extends FirstLevelTree {
|
||||
TagListTree(RootTree root) {
|
||||
super(root, ROOT_BY_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getOpeningStatus() {
|
||||
return Status.ALWAYS_RELOAD_BEFORE_OPENING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForOpening() {
|
||||
clear();
|
||||
for (Tag t : Collection.tags()) {
|
||||
if (t.Parent == null) {
|
||||
createTagSubTree(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBookEvent(BookEvent event, Book book) {
|
||||
switch (event) {
|
||||
case Added:
|
||||
{
|
||||
final List<Tag> bookTags = book.tags();
|
||||
boolean changed = false;
|
||||
if (bookTags.isEmpty()) {
|
||||
changed &= createTagSubTree(Tag.NULL);
|
||||
} else for (Tag t : bookTags) {
|
||||
if (t.Parent == null) {
|
||||
changed &= createTagSubTree(t);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
case Removed:
|
||||
// TODO: implement
|
||||
return false;
|
||||
default:
|
||||
case Updated:
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.geometerplus.fbreader.library;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.geometerplus.fbreader.book.*;
|
||||
|
||||
public final class TagTree extends LibraryTree {
|
||||
|
@ -36,8 +38,8 @@ public final class TagTree extends LibraryTree {
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return Tag != null
|
||||
? Tag.Name : Library.resource().getResource("booksWithNoTags").getValue();
|
||||
return Tag.NULL.equals(Tag)
|
||||
? Library.resource().getResource("booksWithNoTags").getValue() : Tag.Name;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,7 +48,7 @@ public final class TagTree extends LibraryTree {
|
|||
}
|
||||
|
||||
protected String getSortKey() {
|
||||
return Tag != null ? Tag.Name : null;
|
||||
return Tag.NULL.equals(Tag) ? null : Tag.Name;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,7 +56,7 @@ public final class TagTree extends LibraryTree {
|
|||
if (book == null) {
|
||||
return false;
|
||||
}
|
||||
if (Tag == null) {
|
||||
if (Tag.NULL.equals(Tag)) {
|
||||
return book.tags().isEmpty();
|
||||
}
|
||||
for (Tag t : book.tags()) {
|
||||
|
@ -66,4 +68,53 @@ public final class TagTree extends LibraryTree {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getOpeningStatus() {
|
||||
return Status.ALWAYS_RELOAD_BEFORE_OPENING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForOpening() {
|
||||
clear();
|
||||
if (!Tag.NULL.equals(Tag)) {
|
||||
for (Tag t : Collection.tags()) {
|
||||
if (Tag.equals(t.Parent)) {
|
||||
createTagSubTree(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Book book : Collection.books(Tag)) {
|
||||
createBookWithAuthorsSubTree(book);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBookEvent(BookEvent event, Book book) {
|
||||
switch (event) {
|
||||
case Added:
|
||||
{
|
||||
boolean changed = false;
|
||||
final List<Tag> bookTags = book.tags();
|
||||
if (bookTags.isEmpty()) {
|
||||
changed &= Tag.NULL.equals(Tag) && createBookWithAuthorsSubTree(book);
|
||||
} else {
|
||||
for (Tag t : bookTags) {
|
||||
if (Tag.equals(t)) {
|
||||
changed &= createBookWithAuthorsSubTree(book);
|
||||
} else if (Tag.equals(t.Parent)) {
|
||||
changed &= createTagSubTree(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
case Removed:
|
||||
// TODO: implement
|
||||
case Updated:
|
||||
// TODO: implement
|
||||
default:
|
||||
return super.onBookEvent(event, book);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue