X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Ffanfix%2Flibrary%2FBasicLibrary.java;h=7f286079ed1fe87c88f2ec798a85c26774950089;hb=e4e740f55b13b48f849e44c8f9a7ef5b746835c1;hp=4c32e3c2a6cbb68d85efb17a3df113026c82ef4d;hpb=5083ef84efb7e001c0b5de235d3424e1b59c62f4;p=nikiroo-utils.git
diff --git a/src/be/nikiroo/fanfix/library/BasicLibrary.java b/src/be/nikiroo/fanfix/library/BasicLibrary.java
index 4c32e3c..7f28607 100644
--- a/src/be/nikiroo/fanfix/library/BasicLibrary.java
+++ b/src/be/nikiroo/fanfix/library/BasicLibrary.java
@@ -1,12 +1,14 @@
package be.nikiroo.fanfix.library;
-import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
import be.nikiroo.fanfix.Instance;
import be.nikiroo.fanfix.data.MetaData;
@@ -14,8 +16,10 @@ import be.nikiroo.fanfix.data.Story;
import be.nikiroo.fanfix.output.BasicOutput;
import be.nikiroo.fanfix.output.BasicOutput.OutputType;
import be.nikiroo.fanfix.supported.BasicSupport;
-import be.nikiroo.fanfix.supported.BasicSupport.SupportType;
+import be.nikiroo.fanfix.supported.SupportType;
+import be.nikiroo.utils.Image;
import be.nikiroo.utils.Progress;
+import be.nikiroo.utils.StringUtils;
/**
* Manage a library of Stories: import, export, list, modify.
@@ -29,6 +33,44 @@ import be.nikiroo.utils.Progress;
* @author niki
*/
abstract public class BasicLibrary {
+ /**
+ * A {@link BasicLibrary} status.
+ *
+ * @author niki
+ */
+ public enum Status {
+ /** 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. */
+ UNAUTHORIZED,
+ /** The library is currently out of commission. */
+ 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);
+ }
+ }
+
/**
* Return a name for this library (the UI may display this).
*
@@ -40,6 +82,15 @@ abstract public class BasicLibrary {
return "";
}
+ /**
+ * The library status.
+ *
+ * @return the current status
+ */
+ public Status getStatus() {
+ return Status.READ_WRITE;
+ }
+
/**
* Retrieve the main {@link File} corresponding to the given {@link Story},
* which can be passed to an external reader or instance.
@@ -52,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.
@@ -62,21 +116,40 @@ abstract public class BasicLibrary {
* the Library UID of the story
*
* @return the cover image
+ *
+ * @throws IOException
+ * in case of IOException
*/
- public abstract BufferedImage getCover(String luid);
+ public abstract Image getCover(String luid) throws IOException;
+ // TODO: ensure it is the main used interface
+ public synchronized 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.
*
- * By default, return the cover of the first story with this source.
+ * By default, return the custom cover if any, and if not, return the cover
+ * of the first story with this source.
*
* @param source
* the source
*
* @return the cover image or NULL
+ *
+ * @throws IOException
+ * in case of IOException
*/
- public BufferedImage getSourceCover(String source) {
- List metas = getListBySource(source);
+ public Image getSourceCover(String source) throws IOException {
+ Image custom = getCustomSourceCover(source);
+ if (custom != null) {
+ return custom;
+ }
+
+ List metas = getList().filter(source, null, null);
if (metas.size() > 0) {
return getCover(metas.get(0).getLuid());
}
@@ -85,31 +158,143 @@ abstract public class BasicLibrary {
}
/**
- * Fix the source cover to the given story cover.
+ * 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 = getList().filter(null, author, null);
+ if (metas.size() > 0) {
+ return getCover(metas.get(0).getLuid());
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the custom cover image associated to this source.
+ *
+ * By default, return NULL.
+ *
+ * @param source
+ * the source to look for
+ *
+ * @return the custom cover or NULL if none
+ *
+ * @throws IOException
+ * in case of IOException
+ */
+ @SuppressWarnings("unused")
+ public Image getCustomSourceCover(String source) throws IOException {
+ return null;
+ }
+
+ /**
+ * 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
+ */
+ @SuppressWarnings("unused")
+ public Image getCustomAuthorCover(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
* MAY not have the cover included).
+ *
+ * The returned list MUST 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 getMetas(Progress pg);
+ protected abstract List getMetas(Progress pg) throws IOException;
/**
* Invalidate the {@link Story} cache (when the content should be re-read
* because it was changed).
*/
- protected abstract void clearCache();
+ protected void invalidateInfo() {
+ invalidateInfo(null);
+ }
+
+ /**
+ * Invalidate the {@link Story} cache (when the content is removed).
+ *
+ * All the cache can be deleted if NULL is passed as meta.
+ *
+ * @param luid
+ * the LUID of the {@link Story} to clear from the cache, or NULL
+ * for all stories
+ */
+ protected abstract void invalidateInfo(String luid);
+
+ /**
+ * Invalidate the {@link Story} cache (when the content has changed, but we
+ * already have it) with the new given meta.
+ *
+ * @param meta
+ * the {@link Story} to clear from the cache
+ *
+ * @throws IOException
+ * in case of IOException
+ */
+ protected abstract void updateInfo(MetaData meta) throws IOException;
/**
* Return the next LUID that can be used.
@@ -147,34 +332,59 @@ abstract public class BasicLibrary {
throws IOException;
/**
- * Refresh the {@link BasicLibrary}, that is, make sure all stories are
+ * Refresh the {@link BasicLibrary}, that is, make sure all metas are
* loaded.
*
- * @param full
- * force the full content of the stories to be loaded, not just
- * the {@link MetaData}
- *
* @param pg
* the optional progress reporter
*/
- public void refresh(boolean full, Progress pg) {
- if (full) {
- // TODO: progress
- List metas = getMetas(pg);
- for (MetaData meta : metas) {
- getStory(meta.getLuid(), null);
- }
- } else {
+ public synchronized void refresh(Progress 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 true).
+ *
+ * @param luid
+ * the Library UID
+ *
+ * @return TRUE if it is
+ */
+ public boolean isCached(String luid) {
+ // By default, everything is cached
+ return true;
+ }
+
+ /**
+ * Clear the {@link Story} from the cache, if needed.
+ *
+ * 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
+ */
+ 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 getSources() {
+ public synchronized List getSources() throws IOException {
List list = new ArrayList();
for (MetaData meta : getMetas(null)) {
String storySource = meta.getSource();
@@ -187,12 +397,60 @@ abstract public class BasicLibrary {
return list;
}
+ /**
+ * List all the known types (sources) of stories, grouped by directory
+ * ("Source_1/a" and "Source_1/b" will be grouped into "Source_1").
+ *
+ * Note that an empty item in the list means a non-grouped source (type) --
+ * e.g., you could have for Source_1:
+ *
+ * - : empty, so source is "Source_1"
+ * - a: empty, so source is "Source_1/a"
+ * - b: empty, so source is "Source_1/b"
+ *
+ *
+ * @return the grouped list
+ *
+ * @throws IOException
+ * in case of IOException
+ */
+ public synchronized Map> getSourcesGrouped()
+ throws IOException {
+ Map> map = new TreeMap>();
+ for (String source : getSources()) {
+ String name;
+ String subname;
+
+ int pos = source.indexOf('/');
+ if (pos > 0 && pos < source.length() - 1) {
+ name = source.substring(0, pos);
+ subname = source.substring(pos + 1);
+
+ } else {
+ name = source;
+ subname = "";
+ }
+
+ List list = map.get(name);
+ if (list == null) {
+ list = new ArrayList();
+ map.put(name, list);
+ }
+ list.add(subname);
+ }
+
+ return map;
+ }
+
/**
* 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();
@@ -206,62 +464,141 @@ abstract public class BasicLibrary {
}
/**
- * List all the stories in the {@link BasicLibrary}.
+ * Return the list of authors, grouped by starting letter(s) if needed.
*
- * Cover images not included.
+ * If the number of author is not too high, only one group with an empty
+ * name and all the authors will be returned.
+ *
+ * If not, the authors will be separated into groups:
+ *
+ * - *: any author whose name doesn't contain letters nor numbers
+ *
+ * - 0-9: any authors whose name starts with a number
+ * - A-C (for instance): any author whose name starts with
+ * A, B or C
+ *
+ * Note that the letters used in the groups can vary (except * and
+ * 0-9, which may only be present or not).
*
- * @return the stories
+ * @return the authors' names, grouped by letter(s)
+ *
+ * @throws IOException
+ * in case of IOException
*/
- public synchronized List getList() {
- return getMetas(null);
+ public Map> getAuthorsGrouped() throws IOException {
+ int MAX = 20;
+
+ Map> groups = new TreeMap>();
+ List authors = getAuthors();
+
+ // If all authors fit the max, just report them as is
+ if (authors.size() <= MAX) {
+ groups.put("", authors);
+ return groups;
+ }
+
+ // Create groups A to Z, which can be empty here
+ for (char car = 'A'; car <= 'Z'; car++) {
+ groups.put(Character.toString(car), getAuthorsGroup(authors, car));
+ }
+
+ // 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 = keyNow.charAt(0) + "-"
+ + keyNext.charAt(keyNext.length() - 1);
+
+ List all = new ArrayList();
+ 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)
+ }
+ }
+
+ // 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);
+ }
+ }
+
+ return groups;
}
/**
- * List all the stories of the given source type in the {@link BasicLibrary}
- * , or all the stories if NULL is passed as a type.
- *
- * Cover images not included.
+ * Get all the authors that start with the given character:
+ *
+ * - *: any author whose name doesn't contain letters nor numbers
+ *
+ * - 0: any authors whose name starts with a number
+ * - A (any capital latin letter): any author whose name starts
+ * with A
+ *
*
- * @param type
- * the type of story to retrieve, or NULL for all
+ * @param authors
+ * the full list of authors
+ * @param car
+ * the starting character, *, 0 or a capital
+ * letter
*
- * @return the stories
+ * @return the authors that fulfil the starting letter
*/
- public synchronized List getListBySource(String type) {
- List list = new ArrayList();
- for (MetaData meta : getMetas(null)) {
- String storyType = meta.getSource();
- if (type == null || type.equalsIgnoreCase(storyType)) {
- list.add(meta);
+ private List getAuthorsGroup(List authors, char car) {
+ List accepted = new ArrayList();
+ for (String author : authors) {
+ char first = '*';
+ for (int i = 0; first == '*' && i < author.length(); i++) {
+ String san = StringUtils.sanitize(author, true, true);
+ char c = san.charAt(i);
+ if (c >= '0' && c <= '9') {
+ first = '0';
+ } else if (c >= 'a' && c <= 'z') {
+ first = (char) (c - 'a' + 'A');
+ } else if (c >= 'A' && c <= 'Z') {
+ first = c;
+ }
+ }
+
+ if (first == car) {
+ accepted.add(author);
}
}
- Collections.sort(list);
- return list;
+ return accepted;
}
/**
- * List all the stories of the given author in the {@link BasicLibrary}, or
- * all the stories if NULL is passed as an author.
+ * List all the stories in the {@link BasicLibrary}.
*
- * Cover images not included.
- *
- * @param author
- * the author of the stories to retrieve, or NULL for all
+ * Cover images MAYBE not included.
*
* @return the stories
+ *
+ * @throws IOException
+ * in case of IOException
*/
- public synchronized List getListByAuthor(String author) {
- List list = new ArrayList();
- 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);
}
/**
@@ -272,8 +609,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())) {
@@ -294,8 +634,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) {
+ 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,
+ @SuppressWarnings("javadoc") MetaData meta, Progress pg)
+ throws IOException {
+
if (pg == null) {
pg = new Progress();
}
@@ -308,37 +693,31 @@ 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).process(url,
- pgProcess);
- // Because we do not want to clear the meta cache:
- meta.setCover(story.getMeta().getCover());
- 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();
- }
+ 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);
- break;
+ // 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.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();
}
return story;
@@ -353,18 +732,33 @@ 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();
+
+ pg.setMinMax(0, 1000);
+ Progress pgProcess = new Progress();
+ Progress pgSave = new Progress();
+ pg.addProgress(pgProcess, 800);
+ pg.addProgress(pgSave, 200);
+
BasicSupport support = BasicSupport.getSupport(url);
if (support == null) {
- throw new IOException("URL not supported: " + url.toString());
+ throw new UnknownHostException("" + url);
}
- return save(support.process(url, pg), null);
+ Story story = save(support.process(pgProcess), pgSave);
+ pg.setName(story.getMeta().getTitle());
+ pg.done();
+
+ return story.getMeta();
}
/**
@@ -429,7 +823,7 @@ abstract public class BasicLibrary {
pg.addProgress(pgOut, 1);
}
- BasicOutput out = BasicOutput.getOutput(type, false);
+ BasicOutput out = BasicOutput.getOutput(type, false, false);
if (out == null) {
throw new IOException("Output type not supported: " + type);
}
@@ -479,24 +873,37 @@ abstract public class BasicLibrary {
*/
public synchronized Story save(Story story, String luid, Progress pg)
throws IOException {
+ if (pg == null) {
+ pg = new Progress();
+ }
+
+ 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 {
meta.setLuid(luid);
}
- if (getInfo(luid) != null) {
+ if (luid != null && getInfo(luid) != null) {
delete(luid);
}
- doSave(story, pg);
+ story = doSave(story, pg);
+
+ updateInfo(story.getMeta());
- clearCache();
+ Instance.getInstance().getTraceHandler()
+ .trace(this.getClass().getSimpleName() + ": story saved (" + luid + ")");
+ pg.setName(meta.getTitle());
+ pg.done();
return story;
}
@@ -510,8 +917,14 @@ abstract public class BasicLibrary {
* in case of I/O error
*/
public synchronized void delete(String luid) throws IOException {
+ Instance.getInstance().getTraceHandler().trace(this.getClass().getSimpleName() + ": deleting story " + luid);
+
doDelete(luid);
- clearCache();
+ invalidateInfo(luid);
+
+ Instance.getInstance().getTraceHandler()
+ .trace(this.getClass().getSimpleName() + ": story deleted (" + luid
+ + ")");
}
/**
@@ -534,7 +947,83 @@ abstract public class BasicLibrary {
throw new IOException("Story not found: " + luid);
}
+ changeSTA(luid, newSource, meta.getTitle(), meta.getAuthor(), pg);
+ }
+
+ /**
+ * Change the title (name) of the given {@link Story}.
+ *
+ * @param luid
+ * the {@link Story} LUID
+ * @param newTitle
+ * the new title
+ * @param pg
+ * the optional progress reporter
+ *
+ * @throws IOException
+ * in case of I/O error or if the {@link Story} was not found
+ */
+ public synchronized void changeTitle(String luid, String newTitle,
+ Progress pg) throws IOException {
+ MetaData meta = getInfo(luid);
+ if (meta == null) {
+ throw new IOException("Story not found: " + luid);
+ }
+
+ changeSTA(luid, meta.getSource(), newTitle, meta.getAuthor(), pg);
+ }
+
+ /**
+ * Change the author of the given {@link Story}.
+ *
+ * @param luid
+ * the {@link Story} LUID
+ * @param newAuthor
+ * the new author
+ * @param pg
+ * the optional progress reporter
+ *
+ * @throws IOException
+ * in case of I/O error or if the {@link Story} was not found
+ */
+ public synchronized void changeAuthor(String luid, String newAuthor,
+ Progress pg) throws IOException {
+ MetaData meta = getInfo(luid);
+ if (meta == null) {
+ throw new IOException("Story not found: " + luid);
+ }
+
+ changeSTA(luid, meta.getSource(), meta.getTitle(), newAuthor, pg);
+ }
+
+ /**
+ * Change the Source, Title and Author of the {@link Story} in one single
+ * go.
+ *
+ * @param luid
+ * the {@link Story} LUID
+ * @param newSource
+ * the new source
+ * @param newTitle
+ * the new title
+ * @param newAuthor
+ * the new author
+ * @param pg
+ * the optional progress reporter
+ *
+ * @throws IOException
+ * in case of I/O error or if the {@link Story} was not found
+ */
+ protected synchronized void changeSTA(String luid, String newSource,
+ String newTitle, String newAuthor, Progress pg) throws IOException {
+ MetaData meta = getInfo(luid);
+ if (meta == null) {
+ throw new IOException("Story not found: " + luid);
+ }
+
meta.setSource(newSource);
+ meta.setTitle(newTitle);
+ meta.setAuthor(newAuthor);
saveMeta(meta, pg);
}
@@ -545,7 +1034,7 @@ abstract public class BasicLibrary {
* By default, delete the old {@link Story} then recreate a new
* {@link Story}.
*
- * Note that this behaviour can lead to data loss.
+ * Note that this behaviour can lead to data loss in case of problems!
*
* @param meta
* the new {@link MetaData} (LUID MUST NOT change)
@@ -571,8 +1060,8 @@ abstract public class BasicLibrary {
throw new IOException("Story not found: " + meta.getLuid());
}
+ // TODO: this is not safe!
delete(meta.getLuid());
-
story.setMeta(meta);
save(story, meta.getLuid(), pgSet);