X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Ffanfix%2Flibrary%2FBasicLibrary.java;h=3e3756fe90d1677ac2b9820bf300b10cc3b484ea;hb=0bb51c9c66697fe63ba066715207deabbcc1d479;hp=b3b49fdbce50ec5aa6b4877af9c42b2a949e39df;hpb=c1b93db3213970c86b766c92a0b0d546c9d1c2ae;p=fanfix.git diff --git a/src/be/nikiroo/fanfix/library/BasicLibrary.java b/src/be/nikiroo/fanfix/library/BasicLibrary.java index b3b49fd..3e3756f 100644 --- a/src/be/nikiroo/fanfix/library/BasicLibrary.java +++ b/src/be/nikiroo/fanfix/library/BasicLibrary.java @@ -4,12 +4,10 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.net.UnknownHostException; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.TreeMap; import be.nikiroo.fanfix.Instance; @@ -41,14 +39,36 @@ abstract public class BasicLibrary { * @author niki */ public enum Status { - /** The library is ready. */ - READY, + /** The library is ready and r/w. */ + READ_WRITE, + /** The library is ready, but read-only. */ + READ_ONLY, /** The library is invalid (not correctly set up). */ INVALID, /** You are not allowed to access this library. */ - UNAUTORIZED, + UNAUTHORIZED, /** The library is currently out of commission. */ - UNAVAILABLE, + UNAVAILABLE; + + /** + * The library is available (you can query it). + *

+ * It does not specify if it is read-only or not. + * + * @return TRUE if it is + */ + public boolean isReady() { + return (this == READ_WRITE || this == READ_ONLY); + } + + /** + * This library can be modified (= you are allowed to modify it). + * + * @return TRUE if it is + */ + public boolean isWritable() { + return (this == READ_WRITE); + } } /** @@ -68,7 +88,7 @@ abstract public class BasicLibrary { * @return the current status */ public Status getStatus() { - return Status.READY; + return Status.READ_WRITE; } /** @@ -83,8 +103,11 @@ abstract public class BasicLibrary { * the optional {@link Progress} * * @return the corresponding {@link Story} + * + * @throws IOException + * in case of IOException */ - public abstract File getFile(String luid, Progress pg); + public abstract File getFile(String luid, Progress pg) throws IOException; /** * Return the cover image associated to this story. @@ -93,8 +116,11 @@ abstract public class BasicLibrary { * the Library UID of the story * * @return the cover image + * + * @throws IOException + * in case of IOException */ - public abstract Image getCover(String luid); + public abstract Image getCover(String luid) throws IOException; /** * Return the cover image associated to this source. @@ -106,8 +132,11 @@ abstract public class BasicLibrary { * the source * * @return the cover image or NULL + * + * @throws IOException + * in case of IOException */ - public Image getSourceCover(String source) { + public Image getSourceCover(String source) throws IOException { Image custom = getCustomSourceCover(source); if (custom != null) { return custom; @@ -121,6 +150,34 @@ abstract public class BasicLibrary { return null; } + /** + * Return the cover image associated to this author. + *

+ * By default, return the custom cover if any, and if not, return the cover + * of the first story with this author. + * + * @param author + * the author + * + * @return the cover image or NULL + * + * @throws IOException + * in case of IOException + */ + public Image getAuthorCover(String author) throws IOException { + Image custom = getCustomAuthorCover(author); + if (custom != null) { + return custom; + } + + List metas = getListByAuthor(author); + if (metas.size() > 0) { + return getCover(metas.get(0).getLuid()); + } + + return null; + } + /** * Return the custom cover image associated to this source. *

@@ -130,20 +187,60 @@ abstract public class BasicLibrary { * the source to look for * * @return the custom cover or NULL if none + * + * @throws IOException + * in case of IOException */ - public Image getCustomSourceCover(@SuppressWarnings("unused") String source) { + public Image getCustomSourceCover(@SuppressWarnings("unused") String source) + throws IOException { return null; } /** - * Fix the source cover to the given story cover. + * Return the custom cover image associated to this author. + *

+ * By default, return NULL. + * + * @param author + * the author to look for + * + * @return the custom cover or NULL if none + * + * @throws IOException + * in case of IOException + */ + public Image getCustomAuthorCover(@SuppressWarnings("unused") String author) + throws IOException { + return null; + } + + /** + * Set the source cover to the given story cover. * * @param source * the source to change * @param luid * the story LUID + * + * @throws IOException + * in case of IOException */ - public abstract void setSourceCover(String source, String luid); + public abstract void setSourceCover(String source, String luid) + throws IOException; + + /** + * Set the author cover to the given story cover. + * + * @param author + * the author to change + * @param luid + * the story LUID + * + * @throws IOException + * in case of IOException + */ + public abstract void setAuthorCover(String author, String luid) + throws IOException; /** * Return the list of stories (represented by their {@link MetaData}, which @@ -153,8 +250,11 @@ abstract public class BasicLibrary { * the optional {@link Progress} * * @return the list (can be empty but not NULL) + * + * @throws IOException + * in case of IOException */ - protected abstract List getMetas(Progress pg); + protected abstract List getMetas(Progress pg) throws IOException; /** * Invalidate the {@link Story} cache (when the content should be re-read @@ -181,8 +281,11 @@ abstract public class BasicLibrary { * * @param meta * the {@link Story} to clear from the cache + * + * @throws IOException + * in case of IOException */ - protected abstract void updateInfo(MetaData meta); + protected abstract void updateInfo(MetaData meta) throws IOException; /** * Return the next LUID that can be used. @@ -225,17 +328,27 @@ abstract public class BasicLibrary { * * @param pg * the optional progress reporter + * + * @throws IOException + * in case of IOException */ public void refresh(Progress pg) { - getMetas(pg); + try { + getMetas(pg); + } catch (IOException e) { + // We will let it fail later + } } /** * List all the known types (sources) of stories. * * @return the sources + * + * @throws IOException + * in case of IOException */ - public synchronized List getSources() { + public synchronized List getSources() throws IOException { List list = new ArrayList(); for (MetaData meta : getMetas(null)) { String storySource = meta.getSource(); @@ -261,8 +374,12 @@ abstract public class BasicLibrary { * * * @return the grouped list + * + * @throws IOException + * in case of IOException */ - public synchronized Map> getSourcesGrouped() { + public synchronized Map> getSourcesGrouped() + throws IOException { Map> map = new TreeMap>(); for (String source : getSources()) { String name; @@ -293,8 +410,11 @@ abstract public class BasicLibrary { * List all the known authors of stories. * * @return the authors + * + * @throws IOException + * in case of IOException */ - public synchronized List getAuthors() { + public synchronized List getAuthors() throws IOException { List list = new ArrayList(); for (MetaData meta : getMetas(null)) { String storyAuthor = meta.getAuthor(); @@ -325,49 +445,64 @@ abstract public class BasicLibrary { * 0-9, which may only be present or not). * * @return the authors' names, grouped by letter(s) + * + * @throws IOException + * in case of IOException */ - public List>> getAuthorsGrouped() { + public Map> getAuthorsGrouped() throws IOException { int MAX = 20; - List>> groups = new ArrayList>>(); + Map> groups = new TreeMap>(); List authors = getAuthors(); + // If all authors fit the max, just report them as is if (authors.size() <= MAX) { - groups.add(new SimpleEntry>("", authors)); + groups.put("", authors); return groups; } - groups.add(new SimpleEntry>("*", getAuthorsGroup( - authors, '*'))); - groups.add(new SimpleEntry>("0-9", - getAuthorsGroup(authors, '0'))); - + // Create groups A to Z, which can be empty here for (char car = 'A'; car <= 'Z'; car++) { - groups.add(new SimpleEntry>(Character - .toString(car), getAuthorsGroup(authors, car))); + groups.put(Character.toString(car), getAuthorsGroup(authors, car)); } - // do NOT collapse * and [0-9] with the rest - for (int i = 2; i + 1 < groups.size(); i++) { - Entry> now = groups.get(i); - Entry> next = groups.get(i + 1); - int currentTotal = now.getValue().size() + next.getValue().size(); + // Collapse them + List keys = new ArrayList(groups.keySet()); + for (int i = 0; i + 1 < keys.size(); i++) { + String keyNow = keys.get(i); + String keyNext = keys.get(i + 1); + + List now = groups.get(keyNow); + List next = groups.get(keyNext); + + int currentTotal = now.size() + next.size(); if (currentTotal <= MAX) { - String key = now.getKey().charAt(0) + "-" - + next.getKey().charAt(next.getKey().length() - 1); + String key = keyNow.charAt(0) + "-" + + keyNext.charAt(keyNext.length() - 1); + List all = new ArrayList(); - all.addAll(now.getValue()); - all.addAll(next.getValue()); - groups.set(i, new SimpleEntry>(key, all)); - groups.remove(i + 1); - i--; + all.addAll(now); + all.addAll(next); + + groups.remove(keyNow); + groups.remove(keyNext); + groups.put(key, all); + + keys.set(i, key); // set the new key instead of key(i) + keys.remove(i + 1); // remove the next, consumed key + i--; // restart at key(i) } } - for (int i = 0; i < groups.size(); i++) { - if (groups.get(i).getValue().size() == 0) { - groups.remove(i); - i--; + // Add "special" groups + groups.put("*", getAuthorsGroup(authors, '*')); + groups.put("0-9", getAuthorsGroup(authors, '0')); + + // Prune empty groups + keys = new ArrayList(groups.keySet()); + for (String key : keys) { + if (groups.get(key).isEmpty()) { + groups.remove(key); } } @@ -389,9 +524,14 @@ abstract public class BasicLibrary { * @param car * the starting character, *, 0 or a capital * letter + * * @return the authors that fulfill the starting letter + * + * @throws IOException + * in case of IOException */ - private List getAuthorsGroup(List authors, char car) { + private List getAuthorsGroup(List authors, char car) + throws IOException { List accepted = new ArrayList(); for (String author : authors) { char first = '*'; @@ -421,8 +561,11 @@ abstract public class BasicLibrary { * Cover images MAYBE not included. * * @return the stories + * + * @throws IOException + * in case of IOException */ - public synchronized List getList() { + public synchronized List getList() throws IOException { return getMetas(null); } @@ -436,8 +579,12 @@ abstract public class BasicLibrary { * the type of story to retrieve, or NULL for all * * @return the stories + * + * @throws IOException + * in case of IOException */ - public synchronized List getListBySource(String type) { + public synchronized List getListBySource(String type) + throws IOException { List list = new ArrayList(); for (MetaData meta : getMetas(null)) { String storyType = meta.getSource(); @@ -460,8 +607,12 @@ abstract public class BasicLibrary { * the author of the stories to retrieve, or NULL for all * * @return the stories + * + * @throws IOException + * in case of IOException */ - public synchronized List getListByAuthor(String author) { + public synchronized List getListByAuthor(String author) + throws IOException { List list = new ArrayList(); for (MetaData meta : getMetas(null)) { String storyAuthor = meta.getAuthor(); @@ -482,8 +633,11 @@ abstract public class BasicLibrary { * the Library UID of the story * * @return the corresponding {@link Story} + * + * @throws IOException + * in case of IOException */ - public synchronized MetaData getInfo(String luid) { + public synchronized MetaData getInfo(String luid) throws IOException { if (luid != null) { for (MetaData meta : getMetas(null)) { if (luid.equals(meta.getLuid())) { @@ -504,8 +658,53 @@ abstract public class BasicLibrary { * the optional progress reporter * * @return the corresponding {@link Story} or NULL if not found + * + * @throws IOException + * in case of IOException + */ + public synchronized Story getStory(String luid, Progress pg) + throws IOException { + Progress pgMetas = new Progress(); + Progress pgStory = new Progress(); + if (pg != null) { + pg.setMinMax(0, 100); + pg.addProgress(pgMetas, 10); + pg.addProgress(pgStory, 90); + } + + MetaData meta = null; + for (MetaData oneMeta : getMetas(pgMetas)) { + if (oneMeta.getLuid().equals(luid)) { + meta = oneMeta; + break; + } + } + + pgMetas.done(); + + Story story = getStory(luid, meta, pgStory); + pgStory.done(); + + return story; + } + + /** + * Retrieve a specific {@link Story}. + * + * @param luid + * the meta of the story + * @param pg + * the optional progress reporter + * + * @return the corresponding {@link Story} or NULL if not found + * + * @throws IOException + * in case of IOException */ - public synchronized Story getStory(String luid, Progress pg) { + public synchronized Story getStory(String luid, + @SuppressWarnings("javadoc") MetaData meta, Progress pg) + throws IOException { + if (pg == null) { pg = new Progress(); } @@ -518,39 +717,33 @@ abstract public class BasicLibrary { pg.addProgress(pgProcess, 1); Story story = null; - for (MetaData meta : getMetas(null)) { - if (meta.getLuid().equals(luid)) { - File file = getFile(luid, pgGet); - pgGet.done(); - try { - SupportType type = SupportType.valueOfAllOkUC(meta - .getType()); - URL url = file.toURI().toURL(); - if (type != null) { - story = BasicSupport.getSupport(type, url) // - .process(pgProcess); - - // Because we do not want to clear the meta cache: - meta.setCover(story.getMeta().getCover()); - meta.setResume(story.getMeta().getResume()); - story.setMeta(meta); - // - } else { - throw new IOException("Unknown type: " + meta.getType()); - } - } catch (IOException e) { - // We should not have not-supported files in the - // library - Instance.getTraceHandler().error( - new IOException("Cannot load file from library: " - + file, e)); - } finally { - pgProcess.done(); - pg.done(); - } - - break; + File file = getFile(luid, pgGet); + pgGet.done(); + try { + SupportType type = SupportType.valueOfAllOkUC(meta.getType()); + URL url = file.toURI().toURL(); + if (type != null) { + story = BasicSupport.getSupport(type, url) // + .process(pgProcess); + + // Because we do not want to clear the meta cache: + meta.setCover(story.getMeta().getCover()); + meta.setResume(story.getMeta().getResume()); + story.setMeta(meta); + // + } else { + throw new IOException("Unknown type: " + meta.getType()); } + } catch (IOException e) { + // We should not have not-supported files in the + // library + Instance.getTraceHandler().error( + new IOException(String.format( + "Cannot load file of type '%s' from library: %s", + meta.getType(), file), e)); + } finally { + pgProcess.done(); + pg.done(); } return story; @@ -853,6 +1046,8 @@ abstract public class BasicLibrary { meta.setTitle(newTitle); meta.setAuthor(newAuthor); saveMeta(meta, pg); + + invalidateInfo(luid); } /**