430fb2026a256510bb7705eea22ce799005ab48e
1 package be
.nikiroo
.fanfix
.library
;
3 import java
.awt
.image
.BufferedImage
;
5 import java
.io
.IOException
;
7 import java
.util
.ArrayList
;
8 import java
.util
.Collections
;
11 import be
.nikiroo
.fanfix
.Instance
;
12 import be
.nikiroo
.fanfix
.data
.MetaData
;
13 import be
.nikiroo
.fanfix
.data
.Story
;
14 import be
.nikiroo
.fanfix
.output
.BasicOutput
;
15 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
16 import be
.nikiroo
.fanfix
.supported
.BasicSupport
;
17 import be
.nikiroo
.fanfix
.supported
.BasicSupport
.SupportType
;
18 import be
.nikiroo
.utils
.Progress
;
21 * Manage a library of Stories: import, export, list, modify.
23 * Each {@link Story} object will be associated with a (local to the library)
24 * unique ID, the LUID, which will be used to identify the {@link Story}.
26 * Most of the {@link BasicLibrary} functions work on a partial (cover
27 * <b>MAY</b> not be included) {@link MetaData} object.
31 abstract public class BasicLibrary
{
33 * A {@link BasicLibrary} status.
38 /** The library is ready. */
40 /** The library is invalid (not correctly set up). */
42 /** You are not allowed to access this library. */
44 /** The library is currently out of commission. */
49 * Return a name for this library (the UI may display this).
53 * @return the name, or an empty {@link String} if none
55 public String
getLibraryName() {
62 * @return the current status
64 public Status
getStatus() {
69 * Retrieve the main {@link File} corresponding to the given {@link Story},
70 * which can be passed to an external reader or instance.
72 * Do <b>NOT</b> alter this file.
75 * the Library UID of the story
77 * the optional {@link Progress}
79 * @return the corresponding {@link Story}
81 public abstract File
getFile(String luid
, Progress pg
);
84 * Return the cover image associated to this story.
87 * the Library UID of the story
89 * @return the cover image
91 public abstract BufferedImage
getCover(String luid
);
94 * Return the cover image associated to this source.
96 * By default, return the cover of the first story with this source.
101 * @return the cover image or NULL
103 public BufferedImage
getSourceCover(String source
) {
104 List
<MetaData
> metas
= getListBySource(source
);
105 if (metas
.size() > 0) {
106 return getCover(metas
.get(0).getLuid());
113 * Fix the source cover to the given story cover.
116 * the source to change
120 public abstract void setSourceCover(String source
, String luid
);
123 * Return the list of stories (represented by their {@link MetaData}, which
124 * <b>MAY</b> not have the cover included).
127 * the optional {@link Progress}
129 * @return the list (can be empty but not NULL)
131 protected abstract List
<MetaData
> getMetas(Progress pg
);
134 * Invalidate the {@link Story} cache (when the content should be re-read
135 * because it was changed).
137 protected abstract void clearCache();
140 * Return the next LUID that can be used.
142 * @return the next luid
144 protected abstract int getNextId();
147 * Delete the target {@link Story}.
150 * the LUID of the {@link Story}
152 * @throws IOException
153 * in case of I/O error or if the {@link Story} wa not found
155 protected abstract void doDelete(String luid
) throws IOException
;
158 * Actually save the story to the back-end.
161 * the {@link Story} to save
163 * the optional {@link Progress}
165 * @return the saved {@link Story} (which may have changed, especially
166 * regarding the {@link MetaData})
168 * @throws IOException
169 * in case of I/O error
171 protected abstract Story
doSave(Story story
, Progress pg
)
175 * Refresh the {@link BasicLibrary}, that is, make sure all metas are
179 * the optional progress reporter
181 public void refresh(Progress pg
) {
186 * List all the known types (sources) of stories.
188 * @return the sources
190 public synchronized List
<String
> getSources() {
191 List
<String
> list
= new ArrayList
<String
>();
192 for (MetaData meta
: getMetas(null)) {
193 String storySource
= meta
.getSource();
194 if (!list
.contains(storySource
)) {
195 list
.add(storySource
);
199 Collections
.sort(list
);
204 * List all the known authors of stories.
206 * @return the authors
208 public synchronized List
<String
> getAuthors() {
209 List
<String
> list
= new ArrayList
<String
>();
210 for (MetaData meta
: getMetas(null)) {
211 String storyAuthor
= meta
.getAuthor();
212 if (!list
.contains(storyAuthor
)) {
213 list
.add(storyAuthor
);
217 Collections
.sort(list
);
222 * List all the stories in the {@link BasicLibrary}.
224 * Cover images not included.
226 * @return the stories
228 public synchronized List
<MetaData
> getList() {
229 return getMetas(null);
233 * List all the stories of the given source type in the {@link BasicLibrary}
234 * , or all the stories if NULL is passed as a type.
236 * Cover images not included.
239 * the type of story to retrieve, or NULL for all
241 * @return the stories
243 public synchronized List
<MetaData
> getListBySource(String type
) {
244 List
<MetaData
> list
= new ArrayList
<MetaData
>();
245 for (MetaData meta
: getMetas(null)) {
246 String storyType
= meta
.getSource();
247 if (type
== null || type
.equalsIgnoreCase(storyType
)) {
252 Collections
.sort(list
);
257 * List all the stories of the given author in the {@link BasicLibrary}, or
258 * all the stories if NULL is passed as an author.
260 * Cover images not included.
263 * the author of the stories to retrieve, or NULL for all
265 * @return the stories
267 public synchronized List
<MetaData
> getListByAuthor(String author
) {
268 List
<MetaData
> list
= new ArrayList
<MetaData
>();
269 for (MetaData meta
: getMetas(null)) {
270 String storyAuthor
= meta
.getAuthor();
271 if (author
== null || author
.equalsIgnoreCase(storyAuthor
)) {
276 Collections
.sort(list
);
281 * Retrieve a {@link MetaData} corresponding to the given {@link Story},
282 * cover image <b>MAY</b> not be included.
285 * the Library UID of the story
287 * @return the corresponding {@link Story}
289 public synchronized MetaData
getInfo(String luid
) {
291 for (MetaData meta
: getMetas(null)) {
292 if (luid
.equals(meta
.getLuid())) {
302 * Retrieve a specific {@link Story}.
305 * the Library UID of the story
307 * the optional progress reporter
309 * @return the corresponding {@link Story} or NULL if not found
311 public synchronized Story
getStory(String luid
, Progress pg
) {
316 Progress pgGet
= new Progress();
317 Progress pgProcess
= new Progress();
320 pg
.addProgress(pgGet
, 1);
321 pg
.addProgress(pgProcess
, 1);
324 for (MetaData meta
: getMetas(null)) {
325 if (meta
.getLuid().equals(luid
)) {
326 File file
= getFile(luid
, pgGet
);
329 SupportType type
= SupportType
.valueOfAllOkUC(meta
331 URL url
= file
.toURI().toURL();
333 story
= BasicSupport
.getSupport(type
).process(url
,
335 // Because we do not want to clear the meta cache:
336 meta
.setCover(story
.getMeta().getCover());
340 throw new IOException("Unknown type: " + meta
.getType());
342 } catch (IOException e
) {
343 // We should not have not-supported files in the
345 Instance
.getTraceHandler().error(
346 new IOException("Cannot load file from library: "
361 * Import the {@link Story} at the given {@link URL} into the
362 * {@link BasicLibrary}.
365 * the {@link URL} to import
367 * the optional progress reporter
369 * @return the imported {@link Story}
371 * @throws IOException
372 * in case of I/O error
374 public Story
imprt(URL url
, Progress pg
) throws IOException
{
375 BasicSupport support
= BasicSupport
.getSupport(url
);
376 if (support
== null) {
377 throw new IOException("URL not supported: " + url
.toString());
380 return save(support
.process(url
, pg
), null);
384 * Import the story from one library to another, and keep the same LUID.
387 * the other library to import from
391 * the optional progress reporter
393 * @throws IOException
394 * in case of I/O error
396 public void imprt(BasicLibrary other
, String luid
, Progress pg
)
398 Progress pgGetStory
= new Progress();
399 Progress pgSave
= new Progress();
405 pg
.addProgress(pgGetStory
, 1);
406 pg
.addProgress(pgSave
, 1);
408 Story story
= other
.getStory(luid
, pgGetStory
);
410 story
= this.save(story
, luid
, pgSave
);
414 throw new IOException("Cannot find story in Library: " + luid
);
419 * Export the {@link Story} to the given target in the given format.
422 * the {@link Story} ID
424 * the {@link OutputType} to transform it to
426 * the target to save to
428 * the optional progress reporter
430 * @return the saved resource (the main saved {@link File})
432 * @throws IOException
433 * in case of I/O error
435 public File
export(String luid
, OutputType type
, String target
, Progress pg
)
437 Progress pgGetStory
= new Progress();
438 Progress pgOut
= new Progress();
441 pg
.addProgress(pgGetStory
, 1);
442 pg
.addProgress(pgOut
, 1);
445 BasicOutput out
= BasicOutput
.getOutput(type
, false);
447 throw new IOException("Output type not supported: " + type
);
450 Story story
= getStory(luid
, pgGetStory
);
452 throw new IOException("Cannot find story to export: " + luid
);
455 return out
.process(story
, target
, pgOut
);
459 * Save a {@link Story} to the {@link BasicLibrary}.
462 * the {@link Story} to save
464 * the optional progress reporter
466 * @return the same {@link Story}, whose LUID may have changed
468 * @throws IOException
469 * in case of I/O error
471 public Story
save(Story story
, Progress pg
) throws IOException
{
472 return save(story
, null, pg
);
476 * Save a {@link Story} to the {@link BasicLibrary} -- the LUID <b>must</b>
477 * be correct, or NULL to get the next free one.
479 * Will override any previous {@link Story} with the same LUID.
482 * the {@link Story} to save
484 * the <b>correct</b> LUID or NULL to get the next free one
486 * the optional progress reporter
488 * @return the same {@link Story}, whose LUID may have changed
490 * @throws IOException
491 * in case of I/O error
493 public synchronized Story
save(Story story
, String luid
, Progress pg
)
495 // Do not change the original metadata, but change the original story
496 MetaData meta
= story
.getMeta().clone();
499 if (luid
== null || luid
.isEmpty()) {
500 meta
.setLuid(String
.format("%03d", getNextId()));
505 if (getInfo(luid
) != null) {
517 * Delete the given {@link Story} from this {@link BasicLibrary}.
520 * the LUID of the target {@link Story}
522 * @throws IOException
523 * in case of I/O error
525 public synchronized void delete(String luid
) throws IOException
{
531 * Change the type (source) of the given {@link Story}.
534 * the {@link Story} LUID
538 * the optional progress reporter
540 * @throws IOException
541 * in case of I/O error or if the {@link Story} was not found
543 public synchronized void changeSource(String luid
, String newSource
,
544 Progress pg
) throws IOException
{
545 MetaData meta
= getInfo(luid
);
547 throw new IOException("Story not found: " + luid
);
550 meta
.setSource(newSource
);
555 * Save back the current state of the {@link MetaData} (LUID <b>MUST NOT</b>
556 * change) for this {@link Story}.
558 * By default, delete the old {@link Story} then recreate a new
561 * Note that this behaviour can lead to data loss.
564 * the new {@link MetaData} (LUID <b>MUST NOT</b> change)
566 * the optional {@link Progress}
568 * @throws IOException
569 * in case of I/O error or if the {@link Story} was not found
571 protected synchronized void saveMeta(MetaData meta
, Progress pg
)
577 Progress pgGet
= new Progress();
578 Progress pgSet
= new Progress();
579 pg
.addProgress(pgGet
, 50);
580 pg
.addProgress(pgSet
, 50);
582 Story story
= getStory(meta
.getLuid(), pgGet
);
584 throw new IOException("Story not found: " + meta
.getLuid());
587 delete(meta
.getLuid());
590 save(story
, meta
.getLuid(), pgSet
);