Merge branch 'subtree'
[nikiroo-utils.git] / src / be / nikiroo / fanfix / library / BasicLibrary.java
index b8b8185cd50f75b915d732901ce8a79814bb9727..a754c002d3e99b2ecb25df00038599b67b0fa021 100644 (file)
@@ -39,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).
+                * <p>
+                * It does <b>not</b> 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);
+               }
        }
 
        /**
@@ -66,7 +88,7 @@ abstract public class BasicLibrary {
         * @return the current status
         */
        public Status getStatus() {
-               return Status.READY;
+               return Status.READ_WRITE;
        }
 
        /**
@@ -76,13 +98,16 @@ abstract public class BasicLibrary {
         * Do <b>NOT</b> alter this file.
         * 
         * @param luid
-        *            the Library UID of the story
+        *            the Library UID of the story, can be NULL
         * @param pg
         *            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.
@@ -91,8 +116,18 @@ 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;
+
+       // TODO: ensure it is the main used interface
+       public MetaResultList getList(Progress pg) throws IOException {
+               return new MetaResultList(getMetas(pg));
+       }
+
+       // TODO: make something for (normal and custom) not-story covers
 
        /**
         * Return the cover image associated to this source.
@@ -104,14 +139,17 @@ 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;
                }
 
-               List<MetaData> metas = getListBySource(source);
+               List<MetaData> metas = getList().filter(source, null, null);
                if (metas.size() > 0) {
                        return getCover(metas.get(0).getLuid());
                }
@@ -129,14 +167,17 @@ abstract public class BasicLibrary {
         *            the author
         * 
         * @return the cover image or NULL
+        * 
+        * @throws IOException
+        *             in case of IOException
         */
-       public Image getAuthorCover(String author) {
+       public Image getAuthorCover(String author) throws IOException {
                Image custom = getCustomAuthorCover(author);
                if (custom != null) {
                        return custom;
                }
 
-               List<MetaData> metas = getListByAuthor(author);
+               List<MetaData> metas = getList().filter(null, author, null);
                if (metas.size() > 0) {
                        return getCover(metas.get(0).getLuid());
                }
@@ -153,8 +194,12 @@ 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) {
+       @SuppressWarnings("unused")
+       public Image getCustomSourceCover(String source) throws IOException {
                return null;
        }
 
@@ -167,8 +212,12 @@ abstract public class BasicLibrary {
         *            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) {
+       @SuppressWarnings("unused")
+       public Image getCustomAuthorCover(String author) throws IOException {
                return null;
        }
 
@@ -179,29 +228,42 @@ abstract public class BasicLibrary {
         *            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 source
+        * @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);
+       public abstract void setAuthorCover(String author, String luid)
+                       throws IOException;
 
        /**
         * Return the list of stories (represented by their {@link MetaData}, which
         * <b>MAY</b> not have the cover included).
+        * <p>
+        * The returned list <b>MUST</b> be a copy, not the original one.
         * 
         * @param pg
         *            the optional {@link Progress}
         * 
         * @return the list (can be empty but not NULL)
+        * 
+        * @throws IOException
+        *             in case of IOException
         */
-       protected abstract List<MetaData> getMetas(Progress pg);
+       protected abstract List<MetaData> getMetas(Progress pg) throws IOException;
 
        /**
         * Invalidate the {@link Story} cache (when the content should be re-read
@@ -228,8 +290,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.
@@ -274,15 +339,53 @@ abstract public class BasicLibrary {
         *            the optional progress reporter
         */
        public void refresh(Progress pg) {
-               getMetas(pg);
+               try {
+                       getMetas(pg);
+               } catch (IOException e) {
+                       // We will let it fail later
+               }
+       }
+
+       /**
+        * Check if the {@link Story} denoted by this Library UID is present in the
+        * cache (if we have no cache, we default to </tt>true</tt>).
+        * 
+        * @param luid
+        *            the Library UID
+        * 
+        * @return TRUE if it is
+        */
+       public boolean isCached(@SuppressWarnings("unused") String luid) {
+               // By default, everything is cached
+               return true;
+       }
+
+       /**
+        * Clear the {@link Story} from the cache, if needed.
+        * <p>
+        * The next time we try to retrieve the {@link Story}, it may be required to
+        * cache it again.
+        * 
+        * @param luid
+        *            the story to clear
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       @SuppressWarnings("unused")
+       public void clearFromCache(String luid) throws IOException {
+               // By default, this is a noop.
        }
 
        /**
         * List all the known types (sources) of stories.
         * 
         * @return the sources
+        * 
+        * @throws IOException
+        *             in case of IOException
         */
-       public synchronized List<String> getSources() {
+       public List<String> getSources() throws IOException {
                List<String> list = new ArrayList<String>();
                for (MetaData meta : getMetas(null)) {
                        String storySource = meta.getSource();
@@ -308,8 +411,11 @@ abstract public class BasicLibrary {
         * </ul>
         * 
         * @return the grouped list
+        * 
+        * @throws IOException
+        *             in case of IOException
         */
-       public synchronized Map<String, List<String>> getSourcesGrouped() {
+       public Map<String, List<String>> getSourcesGrouped() throws IOException {
                Map<String, List<String>> map = new TreeMap<String, List<String>>();
                for (String source : getSources()) {
                        String name;
@@ -340,8 +446,11 @@ abstract public class BasicLibrary {
         * List all the known authors of stories.
         * 
         * @return the authors
+        * 
+        * @throws IOException
+        *             in case of IOException
         */
-       public synchronized List<String> getAuthors() {
+       public List<String> getAuthors() throws IOException {
                List<String> list = new ArrayList<String>();
                for (MetaData meta : getMetas(null)) {
                        String storyAuthor = meta.getAuthor();
@@ -372,8 +481,11 @@ abstract public class BasicLibrary {
         * <tt>0-9</tt>, which may only be present or not).
         * 
         * @return the authors' names, grouped by letter(s)
+        * 
+        * @throws IOException
+        *             in case of IOException
         */
-       public Map<String, List<String>> getAuthorsGrouped() {
+       public Map<String, List<String>> getAuthorsGrouped() throws IOException {
                int MAX = 20;
 
                Map<String, List<String>> groups = new TreeMap<String, List<String>>();
@@ -448,7 +560,8 @@ abstract public class BasicLibrary {
         * @param car
         *            the starting character, <tt>*</tt>, <tt>0</tt> or a capital
         *            letter
-        * @return the authors that fulfill the starting letter
+        * 
+        * @return the authors that fulfil the starting letter
         */
        private List<String> getAuthorsGroup(List<String> authors, char car) {
                List<String> accepted = new ArrayList<String>();
@@ -480,57 +593,12 @@ abstract public class BasicLibrary {
         * Cover images <b>MAYBE</b> not included.
         * 
         * @return the stories
-        */
-       public synchronized List<MetaData> getList() {
-               return getMetas(null);
-       }
-
-       /**
-        * List all the stories of the given source type in the {@link BasicLibrary}
-        * , or all the stories if NULL is passed as a type.
-        * <p>
-        * Cover images not included.
         * 
-        * @param type
-        *            the type of story to retrieve, or NULL for all
-        * 
-        * @return the stories
-        */
-       public synchronized List<MetaData> getListBySource(String type) {
-               List<MetaData> list = new ArrayList<MetaData>();
-               for (MetaData meta : getMetas(null)) {
-                       String storyType = meta.getSource();
-                       if (type == null || type.equalsIgnoreCase(storyType)) {
-                               list.add(meta);
-                       }
-               }
-
-               Collections.sort(list);
-               return list;
-       }
-
-       /**
-        * List all the stories of the given author in the {@link BasicLibrary}, or
-        * all the stories if NULL is passed as an author.
-        * <p>
-        * Cover images not included.
-        * 
-        * @param author
-        *            the author of the stories to retrieve, or NULL for all
-        * 
-        * @return the stories
+        * @throws IOException
+        *             in case of IOException
         */
-       public synchronized List<MetaData> getListByAuthor(String author) {
-               List<MetaData> list = new ArrayList<MetaData>();
-               for (MetaData meta : getMetas(null)) {
-                       String storyAuthor = meta.getAuthor();
-                       if (author == null || author.equalsIgnoreCase(storyAuthor)) {
-                               list.add(meta);
-                       }
-               }
-
-               Collections.sort(list);
-               return list;
+       public MetaResultList getList() throws IOException {
+               return getList(null);
        }
 
        /**
@@ -538,11 +606,14 @@ abstract public class BasicLibrary {
         * cover image <b>MAY</b> not be included.
         * 
         * @param luid
-        *            the Library UID of the story
+        *            the Library UID of the story, can be NULL
         * 
-        * @return the corresponding {@link Story}
+        * @return the corresponding {@link Story} or NULL if not found
+        * 
+        * @throws IOException
+        *             in case of IOException
         */
-       public synchronized MetaData getInfo(String luid) {
+       public MetaData getInfo(String luid) throws IOException {
                if (luid != null) {
                        for (MetaData meta : getMetas(null)) {
                                if (luid.equals(meta.getLuid())) {
@@ -563,8 +634,12 @@ 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) {
+       public Story getStory(String luid, Progress pg)
+                       throws IOException {
                Progress pgMetas = new Progress();
                Progress pgStory = new Progress();
                if (pg != null) {
@@ -593,13 +668,19 @@ abstract public class BasicLibrary {
         * Retrieve a specific {@link Story}.
         * 
         * @param luid
+        *            the LUID of the story
+        * @param meta
         *            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, MetaData meta, Progress pg) {
+       public synchronized Story getStory(String luid, MetaData meta, Progress pg)
+                       throws IOException {
 
                if (pg == null) {
                        pg = new Progress();
@@ -613,12 +694,21 @@ abstract public class BasicLibrary {
                pg.addProgress(pgProcess, 1);
 
                Story story = null;
-               File file = getFile(luid, pgGet);
+               File file = null;
+
+               if (luid != null && meta != null) {
+                       file = getFile(luid, pgGet);
+               }
+
                pgGet.done();
                try {
-                       SupportType type = SupportType.valueOfAllOkUC(meta.getType());
-                       URL url = file.toURI().toURL();
-                       if (type != null) {
+                       if (file != null) {
+                               SupportType type = SupportType.valueOfAllOkUC(meta.getType());
+                               if (type == null) {
+                                       throw new IOException("Unknown type: " + meta.getType());
+                               }
+
+                               URL url = file.toURI().toURL();
                                story = BasicSupport.getSupport(type, url) //
                                                .process(pgProcess);
 
@@ -626,16 +716,13 @@ abstract public class BasicLibrary {
                                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));
+                       // We should not have not-supported files in the library
+                       Instance.getInstance().getTraceHandler()
+                                       .error(new IOException(String.format(
+                                                       "Cannot load file of type '%s' from library: %s",
+                                                       meta.getType(), file), e));
                } finally {
                        pgProcess.done();
                        pg.done();
@@ -653,14 +740,14 @@ abstract public class BasicLibrary {
         * @param pg
         *            the optional progress reporter
         * 
-        * @return the imported {@link Story}
+        * @return the imported Story {@link MetaData}
         * 
         * @throws UnknownHostException
         *             if the host is not supported
         * @throws IOException
         *             in case of I/O error
         */
-       public Story imprt(URL url, Progress pg) throws IOException {
+       public MetaData imprt(URL url, Progress pg) throws IOException {
                if (pg == null)
                        pg = new Progress();
 
@@ -676,9 +763,10 @@ abstract public class BasicLibrary {
                }
 
                Story story = save(support.process(pgProcess), pgSave);
+               pg.setName(story.getMeta().getTitle());
                pg.done();
 
-               return story;
+               return story.getMeta();
        }
 
        /**
@@ -793,14 +881,19 @@ abstract public class BasicLibrary {
         */
        public synchronized Story save(Story story, String luid, Progress pg)
                        throws IOException {
+               if (pg == null) {
+                       pg = new Progress();
+               }
 
-               Instance.getTraceHandler().trace(
+               Instance.getInstance().getTraceHandler().trace(
                                this.getClass().getSimpleName() + ": saving story " + luid);
 
                // Do not change the original metadata, but change the original story
                MetaData meta = story.getMeta().clone();
                story.setMeta(meta);
 
+               pg.setName("Saving story");
+
                if (luid == null || luid.isEmpty()) {
                        meta.setLuid(String.format("%03d", getNextId()));
                } else {
@@ -815,10 +908,12 @@ abstract public class BasicLibrary {
 
                updateInfo(story.getMeta());
 
-               Instance.getTraceHandler().trace(
-                               this.getClass().getSimpleName() + ": story saved (" + luid
-                                               + ")");
+               Instance.getInstance().getTraceHandler()
+                               .trace(this.getClass().getSimpleName() + ": story saved ("
+                                               + luid + ")");
 
+               pg.setName(meta.getTitle());
+               pg.done();
                return story;
        }
 
@@ -832,15 +927,15 @@ abstract public class BasicLibrary {
         *             in case of I/O error
         */
        public synchronized void delete(String luid) throws IOException {
-               Instance.getTraceHandler().trace(
+               Instance.getInstance().getTraceHandler().trace(
                                this.getClass().getSimpleName() + ": deleting story " + luid);
 
                doDelete(luid);
                invalidateInfo(luid);
 
-               Instance.getTraceHandler().trace(
-                               this.getClass().getSimpleName() + ": story deleted (" + luid
-                                               + ")");
+               Instance.getInstance().getTraceHandler()
+                               .trace(this.getClass().getSimpleName() + ": story deleted ("
+                                               + luid + ")");
        }
 
        /**