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 * Return a name for this library (the UI may display this).
37 * @return the name, or an empty {@link String} if none
39 public String
getLibraryName() {
44 * Retrieve the main {@link File} corresponding to the given {@link Story},
45 * which can be passed to an external reader or instance.
47 * Do <b>NOT</b> alter this file.
50 * the Library UID of the story
52 * the optional {@link Progress}
54 * @return the corresponding {@link Story}
56 public abstract File
getFile(String luid
, Progress pg
);
59 * Return the cover image associated to this story.
62 * the Library UID of the story
64 * @return the cover image
66 public abstract BufferedImage
getCover(String luid
);
69 * Return the cover image associated to this source.
71 * By default, return the cover of the first story with this source.
76 * @return the cover image or NULL
78 public BufferedImage
getSourceCover(String source
) {
79 List
<MetaData
> metas
= getListBySource(source
);
80 if (metas
.size() > 0) {
81 return getCover(metas
.get(0).getLuid());
88 * Fix the source cover to the given story cover.
91 * the source to change
95 public abstract void setSourceCover(String source
, String luid
);
98 * Return the list of stories (represented by their {@link MetaData}, which
99 * <b>MAY</b> not have the cover included).
102 * the optional {@link Progress}
104 * @return the list (can be empty but not NULL)
106 protected abstract List
<MetaData
> getMetas(Progress pg
);
109 * Invalidate the {@link Story} cache (when the content should be re-read
110 * because it was changed).
112 protected abstract void clearCache();
115 * Return the next LUID that can be used.
117 * @return the next luid
119 protected abstract int getNextId();
122 * Delete the target {@link Story}.
125 * the LUID of the {@link Story}
127 * @throws IOException
128 * in case of I/O error or if the {@link Story} wa not found
130 protected abstract void doDelete(String luid
) throws IOException
;
133 * Actually save the story to the back-end.
136 * the {@link Story} to save
138 * the optional {@link Progress}
140 * @return the saved {@link Story} (which may have changed, especially
141 * regarding the {@link MetaData})
143 * @throws IOException
144 * in case of I/O error
146 protected abstract Story
doSave(Story story
, Progress pg
)
150 * Refresh the {@link BasicLibrary}, that is, make sure all stories are
154 * force the full content of the stories to be loaded, not just
155 * the {@link MetaData}
158 * the optional progress reporter
160 public void refresh(boolean full
, Progress pg
) {
163 List
<MetaData
> metas
= getMetas(pg
);
164 for (MetaData meta
: metas
) {
165 getStory(meta
.getLuid(), null);
173 * List all the known types (sources) of stories.
175 * @return the sources
177 public synchronized List
<String
> getSources() {
178 List
<String
> list
= new ArrayList
<String
>();
179 for (MetaData meta
: getMetas(null)) {
180 String storySource
= meta
.getSource();
181 if (!list
.contains(storySource
)) {
182 list
.add(storySource
);
186 Collections
.sort(list
);
191 * List all the known authors of stories.
193 * @return the authors
195 public synchronized List
<String
> getAuthors() {
196 List
<String
> list
= new ArrayList
<String
>();
197 for (MetaData meta
: getMetas(null)) {
198 String storyAuthor
= meta
.getAuthor();
199 if (!list
.contains(storyAuthor
)) {
200 list
.add(storyAuthor
);
204 Collections
.sort(list
);
209 * List all the stories in the {@link BasicLibrary}.
211 * Cover images not included.
213 * @return the stories
215 public synchronized List
<MetaData
> getList() {
216 return getMetas(null);
220 * List all the stories of the given source type in the {@link BasicLibrary}
221 * , or all the stories if NULL is passed as a type.
223 * Cover images not included.
226 * the type of story to retrieve, or NULL for all
228 * @return the stories
230 public synchronized List
<MetaData
> getListBySource(String type
) {
231 List
<MetaData
> list
= new ArrayList
<MetaData
>();
232 for (MetaData meta
: getMetas(null)) {
233 String storyType
= meta
.getSource();
234 if (type
== null || type
.equalsIgnoreCase(storyType
)) {
239 Collections
.sort(list
);
244 * List all the stories of the given author in the {@link BasicLibrary}, or
245 * all the stories if NULL is passed as an author.
247 * Cover images not included.
250 * the author of the stories to retrieve, or NULL for all
252 * @return the stories
254 public synchronized List
<MetaData
> getListByAuthor(String author
) {
255 List
<MetaData
> list
= new ArrayList
<MetaData
>();
256 for (MetaData meta
: getMetas(null)) {
257 String storyAuthor
= meta
.getAuthor();
258 if (author
== null || author
.equalsIgnoreCase(storyAuthor
)) {
263 Collections
.sort(list
);
268 * Retrieve a {@link MetaData} corresponding to the given {@link Story},
269 * cover image <b>MAY</b> not be included.
272 * the Library UID of the story
274 * @return the corresponding {@link Story}
276 public synchronized MetaData
getInfo(String luid
) {
278 for (MetaData meta
: getMetas(null)) {
279 if (luid
.equals(meta
.getLuid())) {
289 * Retrieve a specific {@link Story}.
292 * the Library UID of the story
294 * the optional progress reporter
296 * @return the corresponding {@link Story} or NULL if not found
298 public synchronized Story
getStory(String luid
, Progress pg
) {
303 Progress pgGet
= new Progress();
304 Progress pgProcess
= new Progress();
307 pg
.addProgress(pgGet
, 1);
308 pg
.addProgress(pgProcess
, 1);
311 for (MetaData meta
: getMetas(null)) {
312 if (meta
.getLuid().equals(luid
)) {
313 File file
= getFile(luid
, pgGet
);
316 SupportType type
= SupportType
.valueOfAllOkUC(meta
318 URL url
= file
.toURI().toURL();
320 story
= BasicSupport
.getSupport(type
).process(url
,
324 throw new IOException("Unknown type: " + meta
.getType());
326 } catch (IOException e
) {
327 // We should not have not-supported files in the
329 Instance
.getTraceHandler().error(new IOException(
330 "Cannot load file from library: " + file
, e
));
344 * Import the {@link Story} at the given {@link URL} into the
345 * {@link BasicLibrary}.
348 * the {@link URL} to import
350 * the optional progress reporter
352 * @return the imported {@link Story}
354 * @throws IOException
355 * in case of I/O error
357 public Story
imprt(URL url
, Progress pg
) throws IOException
{
358 BasicSupport support
= BasicSupport
.getSupport(url
);
359 if (support
== null) {
360 throw new IOException("URL not supported: " + url
.toString());
363 return save(support
.process(url
, pg
), null);
367 * Import the story from one library to another, and keep the same LUID.
370 * the other library to import from
374 * the optional progress reporter
376 * @throws IOException
377 * in case of I/O error
379 public void imprt(BasicLibrary other
, String luid
, Progress pg
)
381 Progress pgGetStory
= new Progress();
382 Progress pgSave
= new Progress();
388 pg
.addProgress(pgGetStory
, 1);
389 pg
.addProgress(pgSave
, 1);
391 Story story
= other
.getStory(luid
, pgGetStory
);
393 story
= this.save(story
, luid
, pgSave
);
397 throw new IOException("Cannot find story in Library: " + luid
);
402 * Export the {@link Story} to the given target in the given format.
405 * the {@link Story} ID
407 * the {@link OutputType} to transform it to
409 * the target to save to
411 * the optional progress reporter
413 * @return the saved resource (the main saved {@link File})
415 * @throws IOException
416 * in case of I/O error
418 public File
export(String luid
, OutputType type
, String target
, Progress pg
)
420 Progress pgGetStory
= new Progress();
421 Progress pgOut
= new Progress();
424 pg
.addProgress(pgGetStory
, 1);
425 pg
.addProgress(pgOut
, 1);
428 BasicOutput out
= BasicOutput
.getOutput(type
, false);
430 throw new IOException("Output type not supported: " + type
);
433 Story story
= getStory(luid
, pgGetStory
);
435 throw new IOException("Cannot find story to export: " + luid
);
438 return out
.process(story
, target
, pgOut
);
442 * Save a {@link Story} to the {@link BasicLibrary}.
445 * the {@link Story} to save
447 * the optional progress reporter
449 * @return the same {@link Story}, whose LUID may have changed
451 * @throws IOException
452 * in case of I/O error
454 public Story
save(Story story
, Progress pg
) throws IOException
{
455 return save(story
, null, pg
);
459 * Save a {@link Story} to the {@link BasicLibrary} -- the LUID <b>must</b>
460 * be correct, or NULL to get the next free one.
462 * Will override any previous {@link Story} with the same LUID.
465 * the {@link Story} to save
467 * the <b>correct</b> LUID or NULL to get the next free one
469 * the optional progress reporter
471 * @return the same {@link Story}, whose LUID may have changed
473 * @throws IOException
474 * in case of I/O error
476 public synchronized Story
save(Story story
, String luid
, Progress pg
)
478 // Do not change the original metadata, but change the original story
479 MetaData meta
= story
.getMeta().clone();
482 if (luid
== null || luid
.isEmpty()) {
483 meta
.setLuid(String
.format("%03d", getNextId()));
488 if (getInfo(luid
) != null) {
500 * Delete the given {@link Story} from this {@link BasicLibrary}.
503 * the LUID of the target {@link Story}
505 * @throws IOException
506 * in case of I/O error
508 public synchronized void delete(String luid
) throws IOException
{
514 * Change the type (source) of the given {@link Story}.
517 * the {@link Story} LUID
521 * the optional progress reporter
523 * @throws IOException
524 * in case of I/O error or if the {@link Story} was not found
526 public synchronized void changeSource(String luid
, String newSource
,
527 Progress pg
) throws IOException
{
528 MetaData meta
= getInfo(luid
);
530 throw new IOException("Story not found: " + luid
);
533 meta
.setSource(newSource
);
538 * Save back the current state of the {@link MetaData} (LUID <b>MUST NOT</b>
539 * change) for this {@link Story}.
541 * By default, delete the old {@link Story} then recreate a new
544 * Note that this behaviour can lead to data loss.
547 * the new {@link MetaData} (LUID <b>MUST NOT</b> change)
549 * the optional {@link Progress}
551 * @throws IOException
552 * in case of I/O error or if the {@link Story} was not found
554 protected synchronized void saveMeta(MetaData meta
, Progress pg
)
560 Progress pgGet
= new Progress();
561 Progress pgSet
= new Progress();
562 pg
.addProgress(pgGet
, 50);
563 pg
.addProgress(pgSet
, 50);
565 Story story
= getStory(meta
.getLuid(), pgGet
);
567 throw new IOException("Story not found: " + meta
.getLuid());
570 delete(meta
.getLuid());
573 save(story
, meta
.getLuid(), pgSet
);